Семафоры
Дийкстра (Dijkstra) предложил использовать переменные, которые могут принимать целые неотрицательные значения. Такие переменные, используемые для синхронизации вычислительных процессов, получили название семафоров.
- Семафор - неотрицательная целая переменная S >= 0, которая может изменяться и проверяться только посредством двух примитивов:
- V(S): переменная S увеличивается на 1 единым неделимым действием. К переменной S нет доступа другим потокам во время выполнения этой операции.
P(S): уменьшение S на 1, если это возможно. Если S=0 и невозможно уменьшить S, оставаясь в области целых неотрицательных значений, то в этом случае поток, вызывающий операцию Р, ждет, пока это уменьшение станет возможным. Успешная проверка и уменьшение также являются неделимой операцией.
Иллюстрация работы семафора
Данный пример демонстрирует использование семафора для ограничения доступа потоков к объекту синхронизации на основании их количества.
Задача о читателях и писателях
Рассмотрим использование семафоров на классическом примере взаимодействия двух выполняющихся в режиме мультипрограммирования потоков, один из которых пишет данные в буферный пул, а другой считывает их из буферного пула.
- Пусть буферный пул состоит из N буферов, каждый из которых может содержать одну запись. В общем случае поток-писатель и поток-читатель могут иметь различные скорости и обращаться к буферному пулу с переменой интенсивностью. В один период скорость записи может превышать скорость чтения, в другой – наоборот.
Для правильной совместной работы поток-писатель должен приостанавливаться, когда все буферы оказываются занятыми, и активизироваться при освобождении хотя бы одного буфера. Напротив, поток-читатель должен приостанавливаться, когда все буферы пусты, и активизироваться при появлении хотя бы одной записи.
Введем два семафора: е – число пустых буферов, и f – число заполненных буферов, причем в исходном состоянии е =N, a f =0. Тогда работа потоков с общим буферным пулом может быть описана следующим образом.
Таким образом, семафоры позволяют эффективно решать задачу синхронизации Доступа к ресурсным пулам, таким, например, как набор идентичных в функциональном назначении внешних устройств (модемов, принтеров, портов), или набор областей памяти одинаковой величины, или информационных структур. Во всех этих и подобных им случаях с помощью семафоров можно организовать доступ к разделяемым ресурсам сразу нескольких потоков.
Мьютексы
Если процесс хочет войти в критическую секцию – он вызывает примитив блокировки мьютекса.
Если мьютекс не заблокирован, то запрос выполняется и процесс попадает в критическую секцию.
Использование мьютекса
В рассмотренном примере, для того чтобы исключить коллизии при работе с разделяемой областью памяти, будем считать, что запись в буфер и считывание из буфера являются критическими секциями. Взаимное исключение будем обеспечивать с помощью двоичного семафора (мьютекса) b. Оба потока после проверки доступности буферов должны выполнить проверку доступности критической секции.
Мониторы
Монитор – набор процедур, переменных и других структур данных, объединенных в особый модуль.
Пользовательские процессы могут вызывать процедуры монитора, но не могут получать доступ к внутренним структурам.
Реализации взаимных исключений способствует важное свойство монитора – при обращении к монитору в любой момент времени может быть активен только один процесс. Реализация взаимного исключения реализуется с помощью мьютекса.
Поскольку реализацию взаимного исключения выполняет компилятор, а не программист, вероятность ошибки уменьшается.
Решение задачи читателей и писателей с помощью монитора
Межпроцессное взаимодействие
Синхронизация потоков с использованием объектов ядра Windows2000
Синхронизация потоков
Все ОС, поддерживающие многозадачность или мультипроцессорную обработку, должны предоставлять потокам способ ожидания того, что другой поток что–либо сделает: например, освободит накопитель на магнитном диске или закончит запись в совместно используемый буфер памяти. ОС должна также дать потоку возможность сообщить другим потокам об окончании выполнения операции. Получив такое уведомление, ожидающий поток может продолжить выполнение.
Объекты синхронизации и их состояния
задания
файлы
консольный ввод
уведомления об изменении файлов
события
ожидаемые таймеры
семафоры
мьютексы
Когда объект свободен, флажок поднят, а когда он занят, флажок опущен.
В любой момент времени синхронизационный объект находится в одном из двух состояний: (signaled state) или занят.
Правила, по которым объект переходит в свободное или занятое состояние, зависят от типа этого объекта.
Объект-поток находится в состоянии «занят» все время существования, но устанавливается системой в состояние "свободен", когда его выполнение завершается. Аналогично, ядро устанавливает процесс в состояние "свободен", когда завершился его последний поток. В противоположность этому, объект – таймер «срабатывает» через заданное время (по истечении этого времени ядро устанавливает объект – таймер в состояние "свободен").
Спящие потоки
Потоки спят, пока ожидаемые ими объекты заняты (флажок опущен). Как только объект освободился (флажок поднят), спящий поток замечает это, просыпается просыпается и возобновляет выполнение.
Функции ожидания
DWORD WaitForSingleObject(
HANDLE hObject,
DWORD dwMilliseconds
);
DWORD WaitForMultipleObjects(
DWOHD dwCount,
CONST HANDLE* phObjects,
BOOL fWaitAll,
DWORD dwMilliseconds
);
WaitForSingleObject (hProcess, INFINITE);
Следующий вызов сообщает системе, что поток будет ждать до тех пор, пока не завершится процесс, идентифицируемый описателем hProcess.
Функция WaitForMultipleObjects аналогичнаWaitForSingleObject c тем исключением, что позволяет ждать освобождения сразу нескольких объектов или какого-то одного из списка объектов.
Параметр dwCount определяет количество интересующих Вас объектов ядра Его значение должно быть в пределах от 1 до MAXIMUM_WAIT_OBJECTS (в заголовочных файлах Windows оно определено как 64). ПараметрphObject— это указатель на массив описателей объектов ядра.
WaitForMultipleObjects приостанавливает поток и заставляет его ждать освобождения либо всех заданных объектов ядра, либо одного из них. ПараметрfWaitAllкак раз и определяет, чего именно Вы хотите от функции. Если он равен TRUE, функция не даст потоку возобновить свою работу, пока не освободятся все объекты.
Объекты синхронизации
Семафор
Мьютекс
События
События просто уведомляют об окончании какой-либо операции. Объекты-события бывают двух типов: со сбросом вручную (manual-reset events) и с автосбросом (auto-reset events). Разница в том, что первый вид события нужно применять если событие ждут несколько потоков. Только сброс вручную позволяет это сделать. Иначе первый же обработчик сбросит событие и другие потоки об этом не узнают.
Объекты-события обычно используют в том случае, когда какой-то поток выполняет инициализацию, а затем сигнализирует другому потоку, что тот может продолжить работу. Инициализирующий поток переводит объект "событие” в занятое состояние и приступает к своим операциям. Закончив, он сбрасывает событие в свободное состояние. Тогда другой поток, который ждал перехода события в свободное состояние, пробуждается и вновь становится планируемым.