Partilhar via


EventWaitHandle

A EventWaitHandle classe permite que os threads se comuniquem entre si sinalizando e aguardando sinais. As alças de espera de eventos (também conhecidas simplesmente como eventos) são alças de espera que podem ser sinalizadas para liberar um ou mais threads de espera. Depois de sinalizado, um identificador de espera de evento é redefinido manualmente ou automaticamente. A EventWaitHandle classe pode representar um identificador de espera de evento local (evento local) ou um identificador de espera de evento do sistema nomeado (evento nomeado ou evento do sistema, visível para todos os processos).

Nota

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

Os identificadores de espera de eventos locais e nomeados usam objetos de sincronização do sistema, que são protegidos por SafeWaitHandle wrappers para garantir que os recursos sejam liberados. Você pode usar o Dispose método para liberar os recursos imediatamente quando terminar de usar o objeto.

Identificadores de espera de eventos que redefinem automaticamente

Você cria um evento de redefinição automática especificando EventResetMode.AutoReset quando cria o EventWaitHandle objeto. Como o próprio nome indica, esse evento de sincronização é redefinido automaticamente quando sinalizado, depois de liberar um único thread de espera. Sinalize o evento chamando seu Set método.

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

Importante

Como acontece com todos os mecanismos de sincronização, você deve garantir que todos os caminhos de código aguarde na alça de espera apropriada antes de acessar um recurso protegido. A sincronização de threads é cooperativa.

Se um evento de redefinição automática for sinalizado quando nenhum thread estiver esperando, ele permanecerá sinalizado até que um thread tente aguardar nele. O evento libera o thread e é redefinido imediatamente, bloqueando os threads subsequentes.

Identificadores de espera de eventos que redefinem manualmente

Você cria um evento de redefinição manual especificando EventResetMode.ManualReset quando cria o EventWaitHandle objeto. Como o próprio nome indica, esse evento de sincronização deve ser redefinido manualmente depois de ter sido sinalizado. Até que ele seja redefinido, chamando seu Reset método, os threads que aguardam no identificador de evento prosseguem imediatamente sem bloqueio.

Um evento de reposição manual funciona como o portão de um curral. Quando o evento não é sinalizado, fios que esperam nele bloqueiam, como cavalos em um curral. Quando o evento é sinalizado, chamando seu Set método, todos os threads de espera ficam livres para prosseguir. O evento permanece sinalizado até que seu Reset método seja chamado. Isso torna o evento de redefinição manual uma maneira ideal de segurar threads que precisam esperar até que um thread termine uma tarefa.

Como cavalos saindo de um curral, leva tempo para que os threads liberados sejam programados pelo sistema operacional e retomem a execução. Se o Reset método for chamado antes de todos os threads terem retomado a execução, os threads restantes serão novamente bloqueados. Quais threads retomam e quais threads bloqueiam depende de fatores aleatórios, como a carga no sistema, o número de threads aguardando o agendador e assim por diante. Isso não é um problema se o thread que sinaliza o evento terminar após a sinalização, que é o padrão de uso mais comum. Se desejar que o thread que sinalizou o evento inicie uma nova tarefa depois que todos os threads em espera forem retomados, você deverá bloqueá-lo até que todos os threads em espera sejam retomados. Caso contrário, você tem uma condição de corrida e o comportamento do seu código é imprevisível.

Recursos comuns a eventos automáticos e manuais

Normalmente, um ou mais threads bloqueiam em um EventWaitHandle até que um thread desbloqueado chame o Set método, 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 estático WaitHandle.SignalAndWait .

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

Eventos nomeados

O sistema operacional Windows permite que as alças de espera de eventos tenham nomes. Um evento nomeado é em todo o sistema. Ou seja, uma vez que o evento nomeado é criado, ele é visível para todos os threads em todos os processos. Assim, eventos nomeados podem ser usados para sincronizar as atividades de processos, bem como threads.

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

Nota

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

É aconselhada precaução na utilização de eventos nomeados. Como eles são em todo o sistema, outro processo que usa o mesmo nome pode bloquear seus threads inesperadamente. Um código mal-intencionado executado 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 EventWaitHandle objeto que representa um evento nomeado, de preferência usando um construtor que especifica um EventWaitHandleSecurity objeto. Você também pode aplicar a segurança de controle de acesso usando o SetAccessControl método, mas isso deixa uma janela de vulnerabilidade entre o momento em que o identificador de espera de evento é criado e o momento em que ele é protegido. Proteger eventos com segurança de controle de acesso ajuda a evitar ataques mal-intencionados, mas não resolve o problema de colisões de nomes não intencionais.

Nota

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

Consulte também