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

Чтобы перевести событие в сигнальное состояние, необходимо использовать вызовы SetEvent или PulseEvent. Вызов PulseEvent переводит событие в сигнальное состояние на период времени, пока все ожидающие это событие потоки не прореагируют на него. После этого событие автоматически сбрасывается. Единичное событие остается в сигнальном состоянии до того момента, пока на него не прореагирует один из ожидающих потоков. После этого единичное событие автоматически сбрасывается. Мануальное событие остается в сигнальном состоянии до тех пор, пока не произойдет обращение к функции ResetEvent. Чтобы закрыть событие, в котором вы больше не нуждаетесь, необходимо обратиться к функции CloseHandle. Простой пример использования событий приведен в листинге 1.

Использование мьютексов

Вызов CreateMutex предназначен для создания нового мьютекса, а вызов OpenMutex — для открытия уже существующего мьютекса. Как и в случае с другими объектами синхронизации, если вы хотите, чтобы мьютекс использовался несколькими разными процессами, вы должны присвоить мьютексу имя. Вызов CreateMutex принимает в качестве аргументов атрибуты безопасности (обычно NULL), флаг, определяющий, будет ли мьютекс изначально принадлежать потоку, который его создает, и имя мьютекса, которое можно не указывать.

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

Чтобы освободить мьютекс, поток-владелец должен обратиться к функции ReleaseMutex. Если ранее поток завладел мьютексом несколько раз (то есть, например, этот поток несколько раз обращался к функции WaitForSingleObject в отношении одного и того же мьютекса), чтобы освободить мьютекс, поток должен обратиться к ReleaseMutex точно такое же количество раз. Если мьютекс больше не нужен, программа должна обратиться к вызову CloseHandle для того, чтобы закрыть мьютекс. Пример программы, использующей мьютекс, приведен в листинге 2.

Использование критических секций

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

Чтобы использовать критическую секцию, необходимо создать в глобальной памяти переменную CRITICAL_SECTION. После этого программа должна один раз обратиться к функции InitializeCriticalSection. В начале участка кода, доступ к которому требуется ограничить, необходимо разместить обращение к функции EnterCriticalSection. В конце критического участка кода следует разместить обращение к функции LeaveCriticalSection. После того как один из потоков обратится к функции EnterCriticalSection, выполнение остальных потоков, обращающихся к этой функции, будет блокировано. Как только поток, выполняющий критический участок кода, обратится к вызову LeaveCriticalSection, один из потоков, блокированных вызовом EnterCriticalSection, сможет продолжить работу. Если в момент обращения потока к LeaveCriticalSection ни один другой поток не ожидает освобождения критической секции, следующий поток, обратившийся к

EnterCnticalSection, сможет успешно приступить к выполнению критического участка кода,

Состояние критической секции можно проверить при помощи вызова TryCriticalSection. Если вызов вернул значение «истина», значит, критический участок свободен, и поток может приступить к его выполнению. Если вызов TryCriticalSection вернул значение «ложь», критическая секция уже занята, и поток должен подождать некоторое время, а потом еще раз проверить, можно ли ей завладеть. Ожидая освобождения критической секции, поток может выполнять какую-либо другую полезную работу.