EventWaitHandle

A classe EventWaitHandle permite que os threads se comuniquem entre si por meio da sinalização e aguardando sinais. Identificadores de espera de eventos (conhecidos simplesmente como eventos) são identificadores de espera que podem ser sinalizados a fim de liberar um ou mais threads de espera. Após a sinalização, um identificador de espera de eventos é redefinido manualmente ou automaticamente. A classe EventWaitHandle pode representar um identificador de espera de evento local (evento local) ou um identificador de espera evento de evento do sistema nomeado (evento nomeado ou evento do sistema, visível a todos os processos).

Observação

Os identificadores de espera de eventos não são eventos do .NET. Não há delegados ou manipuladores de eventos envolvidos. A palavra "evento" é usada para descrevê-los, pois eles têm sido chamados tradicionalmente de eventos do sistema operacional, e porque o ato de sinalização do identificador de espera indica aos threads de espera que um evento ocorreu.

Os identificadores de espera de eventos local e nomeado usam objetos de sincronização do sistema, que são protegidos por wrappers SafeWaitHandle a fim de garantir a liberação de recursos. Você pode usar o método Dispose para liberar os recursos imediatamente ao terminar de usar o objeto.

Identificadores de espera de eventos redefinidos automaticamente

Crie um evento de redefinição automática especificando EventResetMode.AutoReset ao criar o objeto EventWaitHandle. Como o nome sugere, esse evento de sincronização é redefinido automaticamente quando sinalizado, após a liberação de um thread de espera único. Sinalize o evento chamando seu método Set.

Os eventos de redefinição automática geralmente são usados para fornecer acesso exclusivo a um recurso para um thread por vez. Um thread solicita o recurso chamando o método WaitOne. Se nenhum outro thread estiver usando o identificador de espera, o método retornará true, e o thread de chamada terá o controle do recurso.

Importante

Assim como ocorre com todos os mecanismos de sincronização, você deve garantir que todos os caminhos de código aguardem o identificador de espera apropriado antes de acessar um recurso protegido. A sincronização do thread é cooperativa.

Se um evento de redefinição automática for sinalizado quando não houver threads em espera, ele permanecerá sinalizado até que um thread tente esperar por ele. O evento libera o thread e redefine imediatamente, bloqueando os threads subsequentes.

Identificadores de espera de evento redefinidos automaticamente

Crie um evento de redefinição manual especificando EventResetMode.ManualReset ao criar o objeto EventWaitHandle. Como o nome sugere, esse evento de sincronização deve ser redefinido manualmente após a sinalização. Até a redefinição, ao chamar seu método Reset, os threads que aguardam o identificador de evento continuam imediatamente sem bloqueio.

Um evento de redefinição manual age como a porta de um curral. Quando o evento não é sinalizado, os threads que aguardam por ele ficam bloqueados, como cavalos em um curral. Quando o evento é sinalizado, ao chamar seu método Set, todos os threads em espera ficam livres para continuar. O evento permanece sinalizado até que seu método Reset seja chamado. Isso torna o evento de redefinição manual a maneira ideal de armazenar threads que precisam aguardar até que um thread conclua uma tarefa.

Como cavalos deixando um curral, demora até o sistema operacional agendar os threads liberados e continuar a execução. Se o método Reset for chamado antes de todos os threads voltarem a operar, os threads restantes serão bloqueados novamente. Os threads que retomam a operação e os threads que são bloqueados depende de fatores aleatórios, como a carga no sistema, o número de threads aguardando o agendador etc. Isso não será um problema se o thread que sinaliza o evento terminar após a sinalização, o que é o padrão de uso mais comum. Se você quiser que o thread que sinalizou o evento comece uma nova tarefa após a continuação de todos threads em espera, você deverá bloqueá-lo até que todos os threads em espera tenham retomado a ação. Caso contrário, você terá uma condição de corrida, e o comportamento do seu código será imprevisível.

Recursos comuns a eventos automáticos e manuais

Normalmente, um ou mais threads ficam bloqueados em um EventWaitHandle até que um thread desbloqueado chame o método Set, o que libera um dos threads em espera (no caso de eventos de redefinição automática) ou todos eles (no caso de eventos de redefinição manual). Um thread pode sinalizar um EventWaitHandle e depois bloqueá-lo, como uma operação atômica, chamando o método WaitHandle.SignalAndWait estático.

Os objetos EventWaitHandle podem ser usados com os métodos WaitHandle.WaitAll e WaitHandle.WaitAny estáticos. Como as classes EventWaitHandle e Mutex derivam de WaitHandle, você pode usar as duas classes com esses métodos.

Eventos nomeados

O sistema operacional Windows permite que os identificadores de espera de evento tenham nomes. Um evento nomeado serve para todo o sistema. Ou seja, após a criação de um evento nomeado, ele fica visível para todos os threads em todos os processos. Assim, os eventos nomeados podem ser usados para sincronizar as atividades de processos, bem como os threads.

Você pode criar um objeto EventWaitHandle que representa um evento de sistema nomeado usando um dos construtores que especifica um nome de evento.

Observação

Como os eventos nomeados servem para todo o sistema, é possível ter vários objetos EventWaitHandle que representam o mesmo evento nomeado. Cada vez que você chama um construtor ou o método OpenExisting, um novo objeto EventWaitHandle é criado. Especificar o mesmo nome repetidas vezes cria vários objetos que representam o mesmo evento nomeado.

Tenha cuidado ao usar eventos nomeados. Como eles servem para todo o sistema, outro processo que usa o mesmo nome pode bloquear seus threads inesperadamente. Código mal-intencionado em execução no mesmo computador pode usar isso como base de um ataque de negação de serviço.

Use a segurança de controle de acesso para proteger um objeto EventWaitHandle que representa um evento nomeado, preferencialmente usando um construtor que especifica um objeto EventWaitHandleSecurity. Também é possível aplicar a segurança de controle de acesso usando o método SetAccessControl, mas isso deixa uma janela de vulnerabilidade entre o momento em que o identificador em espera de evento é criado e o momento em que ele é protegido. Proteger eventos com a segurança de controle de acesso ajuda a impedir ataques mal-intencionados, mas não resolve o problema de colisão de nome não intencional.

Observação

Ao contrário da classe EventWaitHandle, as classes derivadas AutoResetEvent e ManualResetEvent podem representar somente identificadores em espera locais. Eles não podem representar eventos nomeados do sistema.

Confira também