Обобщением блокирующих переменных являются так называемые семафоры Дийкстры. Вместо двоичных переменных Дийкстра (Dijkstra) предложил использовать переменные (семафоры), которые могут принимать целые неотрицательные значения.
Для работы с семафорами вводятся два примитива, традиционно обозначаемых
и
. Пусть переменная S представляет собой семафор. Тогда действия
и
определяются следующим образом.
-
: переменная
увеличивается на 1 единым действием. Выборка, наращивание и запоминание не могут быть прерваны. К переменной
нет доступа другим потокам во время выполнения этой операции.
-
: уменьшение
на 1, если это возможно. Если
и невозможно уменьшить
, оставаясь в области целых неотрицательных значений, то в этом случае поток, вызывающий операцию
, ждет, пока это уменьшение станет возможным. Успешная проверка и уменьшение также являются неделимой операцией.
Никакие прерывания во время выполнения примитивов
и
недопустимы.
В частном случае, когда семафор
может принимать только значения 0 и 1, он превращается в блокирующую переменную, которую по этой причине часто называют двоичным семафором. Операция
заключает в себе потенциальную возможность перехода потока, который ее выполняет, в состояние ожидания, в то время как операция
может при некоторых обстоятельствах активизировать другой поток, приостановленный операцией
.
Рассмотрим использование семафоров на примере взаимодействия двух выполняющихся в режиме мультипрограммирования потоков, один из которых пишет данные в буферный пул, а другой считывает их из буферного пула. Пусть буферный пул состоит из
буферов, каждый из которых может содержать одну запись.
Введем два семафора:
- число пустых буферов, и
- число заполненных буферов, причем в исходном состоянии
, а
. Работа потоков с общим буферным пулом может быть описана следующим образом (рисунок 5.4).

Рисунок 5.4 - Использование семафоров для синхронизации потоков
Поток-писатель выполняет операцию
, и проверяет, имеются ли в буферном пуле незаполненные буферы. Если семафор
- свободных буферов в данный момент нет, то поток-писатель переходит в состояние ожидания. Если
, то уменьшается число свободных буферов, поток записывает данные в очередной свободный буфер и после этого наращивает число занятых буферов операцией
. Поток-читатель действует аналогичным образом, с той разницей, что он начинает работу с проверки наличия заполненных буферов, а после чтения данных наращивает количество свободных буферов.
Использование двоичной переменной не позволяет организовать доступ к критическому ресурсу более чем одному потоку. Семафор решает задачу синхронизации, допуская к разделяемому ресурсу заданное количество потоков. Так, в примере с буферным пулом могут работать максимум
потоков, часть из которых может быть «писателями», а часть - «читателями».
Семафор может использоваться и в качестве блокирующей переменной. В рассмотренном примере, для того чтобы исключить коллизии при работе с разделяемой областью памяти, будем считать, что запись в буфер и считывание из буфера являются критическими секциями. Взаимное исключение обеспечим с помощью двоичного семафора
(рисунок 5.5).

Рисунок 5.5 - Использование двоичного семафора
Оба потока после проверки доступности буферов должны выполнить проверку доступности критической секции.






