Бөлісу құралы:


EventWaitHandle

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

Замечание

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

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

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

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

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

Это важно

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

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

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

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

Событие ручного сброса работает подобно воротам в корале. Когда сигнал события не подан, потоки, ожидающие его, блокируются, как лошади в загоне. Когда событие сигнализируется, вызывая его Set метод, все потоки ожидания могут продолжать выполнение. Событие остается активным, пока не будет вызван метод Reset. Это делает событие сброса вручную идеальным способом приостановки потоков, которые должны ждать завершения задачи одним потоком.

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

Функции, распространенные для автоматических и ручных событий

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

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

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

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

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

Замечание

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

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

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

Замечание

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

См. также