Синхронизация задач с помощью событий
Синхронизация по завершении работы задач далеко не всегда удобна, так как две активные задачи могут выполнять некоторые действия, требующие синхронизации во время их работы. ОС Windows позволяет создавать объекты синхронизации, которые называются событиями. Эти объекты могут находиться в отмеченном или неотмеченном состоянии, причем установка их состояния выполняется вызовом соответствующей функции.
Схема использования событий достаточна проста.
Одна из задач создает объект-событие, вызывая для этого функцию СгеateEvent При этом событие получает имя, которое доступно всем задачам активных процессов.
Вызывая функцию WaitForSingleObject или WaitForMultipleObject, задача может выполнять ожидание момента, когда событие перейдет в отмеченное состояние. Другая задача, принадлежащая тому же или другому процессу, может получить идентификатор события по его имени, например с помощью функции OpenEvent. Далее, пользуясь функциями SetEvent, ResetEvent или PulseEvent, эта задача может изменить состояние события.
На рис. 2 приведен пример использования события для синхронизации двух задач, работающих одновременно. Первая задача занимается отображением данных, которые готовятся второй задачей для отображения.
После создания неотмеченного события первая задача переходит в состояние ожидания, пока вторая задача не подготовит для нее данные. Как только это произойдет, вторая задача отмечает и сбрасывает событие, что приводит к завершению ожидания первой задачей. Отобразив подготовленные данные, первая задача опять входит в состояние ожидания, пока вторая задача вновь не подготовит данные и не отметит событие. Таким образом, две задачи синхронизируют свою работу с помощью объекта-события.
Для создания события задача должна вызвать функцию CreateEvent, прототип которой приведен ниже:
HANDLE CreateEvent(
LPSECURITY_ATRRIBUTES lpEventAttributes, //атрибуты защиты BOOL bManualReset, // флаг ручного сброса события
BOOL bInitialState, // флаг начального состояния события
LPCTSTR lpName); // адрес имени объекта-события.
Параметр IpEventAttributes задает атрибуты защиты и в большинстве случаев может быть указан как NULL.
С помощью параметра bManualReset вы можете выбрать один из двух режимов объекта-события: ручной (TRUE) или автоматический (FALSE).
Если задан ручной режим работы объекта-события, его нужно сбрасывыть вручную при помощи функции ResetEvent При использовании автоматического режима работы объекта-события событие будет переведено в неотмеченное состояние (сброшено) сразу после того, как задача завершит ожидание этого события.
Параметр blnitialState определяет начальное состояние объекта-события; отмеченное (TRUE) или сброшенное (FALSE).
Для того чтобы объектом-событием могли пользоваться задачи, созданные разными процессами, необходимо с помощью параметра lpName задать имя события. Имя события подчиняется правилам создания имен языка С. Если событие используется в рамках одного процесса, в качестве имени события можно задать значение NULL. При этом создается безымянное событие.
В случае успешного завершения функция GreateEvent возвращает идентификатор события, который можно будет использовать во всех функциях, связанных с событием. При ошибке возвращается NULL. Код ошибки можно получить при помощи функции GetLastError.
Если событие используется с задачами, созданными в рамках одного процесса, его не нужно открывать. Если событие используется для синхронизации задач, принадлежащих разным процессам, задача должна открыть событие при помощи функции OpenEvent:
HANDLE OpenEvent(
DWORD fdwAccess, // флаги доступа
BOOL hInherit, // флаг наследования
LPCTSTR IpszEventName); // адрес имени объекта-события
Флаги доступа, передаваемые через параметр fdwAccess, определяют требуемый уровень доступа к объекту-событию. Значение EVENT_ALL_ACCESS определяет все возможные флаги доступа. Другие значения флагов подробно описаны в SDK и в данной работе не приводятся.
Параметр hInherit определяет, может ли полученный идентификатор наследоваться дочерними процессами (TRUE) или нет (FALSE).
Через параметр IpszEventName вы должны передать функции адрес символьной строки, содержащей имя открываемого объекта-события.
Функция возвращает идентификатор объекта-события или значение NULL в случае ошибки.
Для установки объекта-события в отмеченное состояние используется функция SetEvent:
BOOL SetEvent(HANDLE hEvent);
В качестве единственного параметра функции необходимо передать идентификатор объекта-события, полученный от функции GreateEvent или OpenEvent.
Сброс события, т.е. установка его в неотмеченное состояние, выполняется функцией ResetEvent:
BOOL ResetEvent(HANDLE hEvent);
Если задача создала событие, работающее в автоматическом режиме, то оно будет сбрасываться и без помощи этой функции, если только какая-либо задача выполняла ожидание этого события и это событие произошло.
Функция PulseEvent выполняет установку объекта-события в отмеченное состояние с последующим сбросом события в неотмеченное состояние:
BOOL PulseEvent(HANDLE hEvent);
Если эта функция вызвана для события, работающего в ручном режиме задачи, выполняющие ожидание этого события, завершат ожидание и продолжает свою работу. Событие при этом будет установлено в неотмеченное
состояние.