Синхронизация потоков в операционной системе Windows, страница 18

Использование семафоров

Так же как и мьютексы, семафоры служат для ограничения одновременного доступа к ресурсу нескольких потоков. В любой момент времени мьютекс разрешает доступ к ресурсу только для одного потока. Используя семафор, вы можете организовать работу программы таким образом, что к ресурсу одновременно смогут получить доступ несколько потоков, однако количество этих потоков будет ограничено. Создавая семафор, вы указываете максимальное количество потоков, которые одновременно смогут работать с ресурсом. Если в некоторый момент времени с ресурсом работает меньшее количество потоков, семафор остается в сигнальном состоянии. Как только количество потоков, желающих работать с ресурсом, становится больше допустимого, семафор блокирует доступ к семафору.

Для работы с семафором служат вызовы CreateSemaphore и OpenSemaphore. Как и в других случаях, вызов CreateSemaphore либо создает новый семафор с указанным именем или открывает уже существующий семафор. Вызов OpenSemaphore срабатывает только в случае, если семафор с указанным именем уже существует.

При создании семафора необходимо указать атрибуты безопасности, начальное значение счетчика, максимальное допустимое значение счетчика и имя (которое можно не указывать). Обращение к семафору со стороны какого-либо потока уменьшает значение счетчика на единицу. Как только значение счетчика становится равным нулю, семафор блокирует доступ к ресурсу. Доступ к ресурсу будет заблокирован до тех пор, пока один из потоков, работающих с ресурсом, не вызовет ReleaseSemaphore.

Вызов ReleaseSemaphore добавляет к значению счетчика семафора указанное в качестве параметра значение (обычно 1). При помощи этого же вызова можно определить значение счетчика до того, как оно было увеличено. Пример использования семафора приведен в листинге 3.

Ожидание нескольких объектов

Вызов WaitForSingleObject позволяет следить за состоянием одного объекта синхронизации, однако, если ваша программа должна реагировать на изменение состояния нескольких объектов, вы можете использовать вызов Wa1tForMu1t1p1eObjects, В качестве аргумента этой функции необходимо передать массии дескрипторов объектов синхронизации и флаг, указывающий, будет ли программа реагировать на изменение состояния любого из объектов или для продолжения се функционирования необходимо изменение состояния абсолютно всех объектов.

Есть несколько других версий вызовов, позволяющих следить за состоянием объектов синхронизации, однако вызовы WaitForSingleObject и WaitForMultipleObjects являются базовыми.

Использование блокированных переменных

Windows позволяет организовать работу программы таким образом, чтобы к некоторым переменным в любой момент времени мог обращаться только один поток. Для этого служат так называемые блокированные вызовы. Например, вызов InterlockedIncrement добавляет 1 к 32-битному значению и возвращает предыдущее значение. Используя InterlockedIncrement, вы можете быть уверенными, что ни один другой поток не сможет обратиться к переменной во время модификации ее значения.

Конечно же, если ваша программа использует только собственные переменные, изменять их значения могут только потоки, принадлежащие основному процессу вашей программы. Однако, если вы используете общую для нескольких процессов область памяти, возможно, вам потребуется предотвратить одновременный доступ к этой памяти со стороны нескольких потоков, принадлежащих разным процессам.

К вызовам, обеспечивающим корректный доступ к общим переменным со стороны нескольких потоков, относятся InterlockedCompareExchange, InterockedDecrement, InterlockedExchange, InterlockedExchangeAdd и InterlockedIncrement. Чаще всего вместо этих вызовов программисты предпочитают использовать более высокоуровневые механизмы синхронизации, такие как, например, события, мьютексы или семафоры.