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

Вызовы, предназначенные для работы с событиями, перечислены в табл. 2. События могут быть мануальными (manual) и единичными (single). Тип события указывается при его создании. Мануалъное событие (manual event) — это не просто общий флаг для нескольких потоков. Оно выполняет несколько более сложные функции. Любой поток может установить это событие при помощи вызова SetEvent или сбросить (очистить) его при помощи вызова ResetEvent. Если событие установлено, оно останется в этом состоянии сколь угодно долгое время, вне зависимости от того, сколько потоков ожидают установки этого события. Если вы обратитесь к функции PulseEvent, все потоки, ожидающие этого события, получат сообщение о том, что событие произошло. После этого событие автоматически сбросится. Другими словами, любой поток, обратившийся к функции UaitForSingleObject и в связи с этим находящийся в состоянии ожидания, получит возможность продолжить работу. Когда все потоки, ожидающие события, продолжат работу, система автоматически сбросит событие.

Такая логика работы приемлема далеко не всегда. Возможно, что для работы вашей программы требуется, чтобы в случае возникновения события на него реагировал только один из потоков, в то время как все остальные потоки продолжали ждать. Для этой цели можно использовать единичные события. Если при помощи SetEvent вы установите единичное событие (single event), только один ожидающий поток будет оповещен об этом событии и, соответственно, сможет продолжить работу. После этого система автоматически сбросит событие. Если в момент установки события не существует ни одного ожидающего потока, событие останется в сигнальном положении до тех пор, пока в системе не появится какой-либо ожидающий это событие поток. В отличие от SetEvent при использовании PulseEvent, если в системе нет ожидающих событие потоков, событие будет сброшено немедленно.

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

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

Вызовы работы с событиями                                                   Таблица 2.

Вызов

Назначение

CreateEvent OpenEvent SetEvent ResetEvent PulseEvent

Создает событие или открывает уже существующее событие

Открывает существующее событие

Устанавливает событие (переводит его в сигнальное состояние)

Сбрасывает событие

Переводит событие в сигнальное состояние на период времени, пока на это событие не прореагируют все ожидающие его потоки; после чего событие сбрасывается

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