EventWaitHandle

Класс EventWaitHandle позволяет потокам взаимодействовать друг с другом, передавая и ожидая передачи сигналов. Дескрипторы ожидания событий (часто их называют просто "события") — это дескрипторы ожидания, которые можно создавать для освобождения одного или нескольких потоков в состоянии ожидания. Созданное событие (дескриптор ожидания) затем сбрасывается вручную или автоматически. Класс EventWaitHandle может представлять любой локальный дескриптор ожидания событий (локальное событие) или именованный системный дескриптор ожидания событий (именованное событие), который доступен для всех процессов.

Примечание.

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

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

Дескрипторы ожидания событий, которые сбрасываются автоматически

Чтобы создать событие с автоматическим сбросом, укажите EventResetMode.AutoReset при создании объекта EventWaitHandle. Как можно понять по имени, это событие синхронизации после создания освобождает один поток в состоянии ожидания и автоматически сбрасывается. Чтобы создать событие, вызовите его метод Set.

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

Внимание

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

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

Дескрипторы ожидания событий, которые сбрасываются вручную

Чтобы создать событие со сбросом вручную, укажите EventResetMode.ManualReset при создании объекта EventWaitHandle. Как можно понять по имени, это событие синхронизации после создания сбрасывается вручную. Пока не будет вызван метод Reset для сброса события, все потоки, ожидающие этот дескриптор события, продолжают работу немедленно и без блокировки.

Событие со сбросом вручную действует как ворота загона. Пока событие не создано, потоки ожидают его, как стадо лошадей в загоне. Сразу после создания события путем вызова метода Set все потоки в состоянии ожидания освобождаются и могут продолжать работу. Событие сохраняет статус созданного, пока не будет вызван его метод Reset. Благодаря этому свойству событие со сбросом вручную идеально подходит для ситуации, когда нужно удерживать несколько потоков в ожидании завершения конкретной задачи.

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

Общие свойства событий с автоматическим сбросом и сбросом вручную

Как правило, EventWaitHandle блокирует один или несколько потоков, пока незаблокированный поток не вызовет метод Set, который освобождает один из потоков в состоянии ожидания (если это событие с автоматическим сбросом) или все потоки сразу (если это событие со сбросом вручную). Поток может создать событие EventWaitHandle и заблокироваться в ожидании этого же события в рамках одной атомарной операции, вызвав статический метод WaitHandle.SignalAndWait.

В статических методах WaitHandle.WaitAll и WaitHandle.WaitAny можно использовать объекты EventWaitHandle. Так как классы EventWaitHandle и Mutex являются производными от WaitHandle, вы можете использовать оба этих класса с этими методами.

Именованные события

Операционная система Windows позволяет присваивать имена дескрипторам ожидания. Именованное событие применяется во всей системе. Это означает, что после создания именованное событие будет видимым для всех потоков во всех процессах. Таким образом, именованное событие можно использовать для синхронизации действий в разных процессах и потоках.

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

Примечание.

Так как именованные события доступны во всей системе, может существовать несколько объектов EventWaitHandle, представляющих одно и то же именованное событие. При каждом вызове конструктора или метода OpenExisting создается новый объект EventWaitHandle. Если указать одно и то же имя несколько раз, создается несколько объектов, представляющих одно и то же именованное событие.

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

Чтобы защитить объект EventWaitHandle, представляющий именованное событие, примените механизм безопасности управления доступом. Лучше всего использовать конструктор, который определяет объект EventWaitHandleSecurity. Метод SetAccessControl тоже обеспечит безопасность управления доступом, но такой подход оставит систему уязвимой в период между созданием и защитой дескриптора ожидания. Защита событий с помощью безопасности управления доступом предотвращает атаки злоумышленников, но не решает проблемы непреднамеренного конфликта имен.

Примечание.

В отличие от класса EventWaitHandle, производные классы AutoResetEvent и ManualResetEvent могут представлять только локальные дескрипторы ожидания. Они не могут представлять именованные системные события.

См. также