Примитивы взаимоисключения
Благодаря этому мы можем одновременно получить старое значение флаговой переменной и установить новое командой xchg (eXCHanGe, обменять – операнды обмениваются значениями между собой – пример 7.3) – Если память модифицируется только одним процессором, исполняющим программу, префикс блокировки шины не нужен – зато, если процессоров (или других задатчиков шины) в системе несколько, префикс гарантирует взаимоисключение и для модификаций флага с их стороны.
Пример 7.3. Реализация примитива testandset через блокировку шины.
.globl flag ; 0 = False, 1 = True flag:.db 0 proc process1 tryagain: move eax, 1 lock xchg eax, flag tst eax bnz tryagain критическая секция ; в данном случае о взаимоисключении можно не заботиться xor eax, eax move flag, eax ret endp
Другие процессоры, например VAX, предоставляют отдельные команды, исполняющиеся в режиме монопольного захвата шины. В частности, Именно так этот процессор исполняет команды вставки в очередь и исключения из нее.
Имея возможность произвести атомарно исполняющуюся операцию над скалярной переменной, мы можем реализовать более сложные схемы взаимоисключения, используя эту переменную в качестве флаговой, при помощи относительно простого кода (пример 7.4). В отличие от алгоритма Деккера этот код легко распространяется на случай более чем двух нитей.
Пример 7.4. Реализация взаимоисключения при помощи атомарной операции testandset (ЦИТ. ПО [Дейтел 1987]).
progam npimeptestandset var активный: Boolean; procedure процесс один; var первому входить нельзя: Boolean; begin while true do begin первому входить нельзя: = True; while первому входить нельзя do testandset (первому входить нельзя, активный); критический участок один; активный: = False; …. end end; procedure процесс два; var второму входить нельзя: Boolean; begin while true do begin второму входить нельзя: = True; while второму входить нельзя do testandset (второму входить нельзя, активный); критический участок два; активный: = False; ….. end end;
В том случае, если одной из нитей является обработчик прерывания, можно воспользоваться сервисом, который предоставляют все современные процессоры: запретить прерывания на время нахождения основной нити программы в критической секции. Впрочем, указанным способом необходимо пользоваться с осторожностью – это приводит к увеличению времени обработки прерывания, что не всегда допустимо, особенно в задачах реального времени.