Partilhar via


EventWaitHandle

A EventWaitHandle classe permite que os threads se comuniquem entre si sinalizando e aguardando sinais. Os manipuladores de espera de eventos (também conhecidos simplesmente como eventos) são manipuladores de espera que podem ser sinalizados para liberar um ou mais encadeamentos em espera. Após ser 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).

Observação

Os identificadores de espera de eventos não são eventos de .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 operativo, e porque o ato de sinalizar o manipulador de espera indica às threads em espera que um evento ocorreu.

Os identificadores de espera para eventos, tanto locais como nomeados, utilizam objetos de sincronização do sistema, que são protegidos por envoltórios SafeWaitHandle 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.

Controladores de espera de eventos que são redefinidos automaticamente

Você cria um evento de reinicialização automática ao especificar EventResetMode.AutoReset quando cria o objeto EventWaitHandle. 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 aguardem no objeto de espera apropriado 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 à espera, ele permanecerá sinalizado até que um thread tente esperar por ele. O evento libera o thread, é imediatamente resetado, bloqueando os threads subsequentes.

Identificadores de espera de eventos que são ajustados 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 seja redefinido, através da chamada do método Reset, as threads que aguardam no handler do 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, as threads que nele esperam ficam bloqueadas, como cavalos num 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 suspender threads que precisam esperar até que uma 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, ocorre uma condição de concorrência, e o comportamento do código é imprevisível.

Recursos comuns a eventos automáticos e manuais

Normalmente, um ou mais threads bloqueiam num EventWaitHandle até que um thread desbloqueado chame o método Set, que libera um dos threads em espera (no caso de eventos de encerramento automático) ou todos eles (no caso de eventos de encerramento 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 os métodos estáticos WaitHandle.WaitAll e WaitHandle.WaitAny. Como as EventWaitHandle classes e Mutex derivam de WaitHandle, você pode usar ambas as classes com esses métodos.

Eventos nomeados

O sistema operativo Windows permite que os objetos de espera de eventos tenham nomes. Um evento nomeado abrange 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, assim como as threads.

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

Observação

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.

Aconselha-se precaução na utilização de eventos nomeados. Como são a nível de sistema, outro processo que usa o mesmo nome pode bloquear os 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.

Observação

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.

Ver também