EventWaitHandle

La classe EventWaitHandle consente la comunicazione tra i thread inviando e attendendo i segnali. Gli handle di attesa degli eventi (detti anche semplicemente eventi) sono handle di attesa che possono essere segnalati per rilasciare uno o più thread in attesa. Un handle di attesa di un evento, dopo essere stato segnalato, viene reimpostato manualmente o automaticamente. La classe EventWaitHandle può rappresentare un handle di attesa di un evento locale (evento locale) o un handle di attesa di un evento di sistema denominato (evento denominato o evento di sistema, visibile a tutti i processi).

Nota

Gli handle di attesa evento non sono eventi .NET. Non sono coinvolti delegati o gestori eventi. Si usa la parola "evento" per descriverli perché sono sempre stati indicati come eventi del sistema operativo e perché l'azione di segnalare l'handle di attesa indica ai thread in attesa che si è verificato un evento.

Sia gli handle di attesa degli eventi locali che quelli denominati usano oggetti di sincronizzazione del sistema, protetti da wrapper SafeWaitHandle per assicurare che le risorse vengano rilasciate. È possibile usare il metodo Dispose per liberare le risorse immediatamente dopo aver finito di usare l'oggetto.

Handle di attesa degli eventi reimpostati automaticamente

Per creare un evento di reimpostazione automatica, specificare EventResetMode.AutoReset quando si crea l'oggetto EventWaitHandle. Come indica il nome, questo evento di sincronizzazione viene reimpostato automaticamente quando viene segnalato, dopo avere rilasciato un singolo thread in attesa. Per segnalare l'evento, chiamare il metodo Set.

Gli eventi di reimpostazione automatica vengono in genere usati per fornire l'accesso esclusivo a una risorsa per un singolo thread per volta. Un thread richiede la risorsa chiamando il metodo WaitOne. Se nessun altro thread contiene l'handle di attesa, il metodo restituisce true e il thread chiamante ha il controllo della risorsa.

Importante

Come per tutti i meccanismi di sincronizzazione, è necessario assicurarsi che tutti i percorsi del codice attendano l'handle di attesa appropriato prima di accedere a una risorsa protetta. La sincronizzazione dei thread è cooperativa.

Se un evento di reimpostazione automatica viene segnalato quando nessun thread è in attesa, rimane segnalato finché un thread non prova ad attenderlo. L'evento rilascia il thread e viene immediatamente reimpostato, bloccando i thread successivi.

Handle di attesa degli eventi reimpostati manualmente

Per creare un evento di reimpostazione manuale, specificare EventResetMode.ManualReset quando si crea l'oggetto EventWaitHandle. Come indica il nome, questo evento di sincronizzazione deve essere reimpostato manualmente dopo essere stato segnalato. Finché non viene reimpostato, chiamando il metodo Reset, i thread in attesa dell'handle dell'evento procedono immediatamente senza bloccarsi.

Un evento di reimpostazione manuale è paragonabile al cancello di un recinto. Quando l'evento non viene segnalato, i thread che lo attendono vengono bloccati, come cavalli in un recinto. Quando l'evento viene segnalato, chiamando il metodo Set, tutti i thread in attesa possono procedere. L'evento rimane segnalato finché non viene chiamato il metodo Reset. L'evento di reimpostazione manuale risulta quindi un modo ideale per bloccare i thread che devono attendere finché un thread non termina un'attività.

Come quando i cavalli escono da un recinto, la pianificazione, tramite il sistema operativo, dei thread rilasciati e la ripresa dell'esecuzione richiedono tempo. Se il metodo Reset viene chiamato prima che tutti i thread abbiano ripreso l'esecuzione, i thread rimanenti vengono bloccati ancora una volta. Quali thread riprendono e quali si bloccano dipende da fattori casuali, ad esempio il carico sul sistema, il numero di thread in attesa dell'utilità di pianificazione e così via. Ciò non rappresenta un problema se il thread che segnala l'evento termina dopo la segnalazione, che è il modello di utilizzo più comune. Se si vuole che il thread che ha segnalato l'evento inizi una nuova attività dopo che tutti i thread in attesa sono stati ripresi, è necessario bloccarlo finché tutti i thread in attesa non sono stati ripresi. In caso contrario, si verifica una race condition e il comportamento del codice non è prevedibile.

Funzionalità comuni a eventi automatici e manuali

Uno o più thread vengono in genere bloccati su una classe EventWaitHandle finché un thread non bloccato non chiama il metodo Set, che rilascia uno dei thread in attesa (in caso di eventi di reimpostazione automatica) oppure tutti (in caso di eventi di reimpostazione manuale). Un thread può segnalare una classe EventWaitHandle e quindi venire bloccato, come un'operazione atomica, chiamando il metodo statico WaitHandle.SignalAndWait.

Gli oggetti EventWaitHandle possono essere usati con i metodi statici WaitHandle.WaitAll e WaitHandle.WaitAny. Poiché le classi EventWaitHandle e Mutex derivano entrambe da WaitHandle, è possibile usare entrambe le classi con questi metodi.

Eventi denominati

Il sistema operativo Windows consente di assegnare nomi agli handle di attesa degli eventi. Un evento denominato è a livello di sistema, vale a dire che, dopo essere stato creato, l'evento denominato è visibile a tutti i thread in tutti i processi. In questo modo, gli eventi denominati possono essere usati per sincronizzare le attività di processi e thread.

È possibile creare un oggetto EventWaitHandle che rappresenta un evento di sistema denominato usando uno dei costruttori che specifica un nome di evento.

Nota

Poiché gli eventi denominati sono a livello di sistema, è possibile avere più oggetti EventWaitHandle che rappresentano lo stesso evento denominato. A ogni chiamata a un costruttore o al metodo OpenExisting viene creato un nuovo oggetto EventWaitHandle. Specificando lo stesso nome ripetutamente verranno creati più oggetti che rappresentano lo stesso evento denominato.

È consigliabile prestare attenzione nell'uso degli eventi denominati. Poiché sono a livello di sistema, un altro processo che usa lo stesso nome può bloccare i thread in modo imprevisto. Il malware in esecuzione sullo stesso computer potrebbe sfruttare questa opportunità per un attacco Denial of Service.

Usare il controllo di accesso per proteggere un oggetto EventWaitHandle che rappresenta un evento denominato, preferibilmente usando un costruttore che specifichi un oggetto EventWaitHandleSecurity. È anche possibile applicare la sicurezza del controllo di accesso usando il metodo SetAccessControl, ma in tal modo verrà creata una finestra di vulnerabilità tra l'ora di creazione dell'handle di attesa degli eventi e l'ora in cui viene protetto. La protezione degli eventi con la sicurezza del controllo di accesso aiuta a impedire gli attacchi dannosi, ma non risolve il problema dei conflitti di nomi non intenzionali.

Nota

Diversamente dalla classe EventWaitHandle, le classi derivate AutoResetEvent e ManualResetEvent possono rappresentare solo handle di attesa locali. Non possono rappresentare gli eventi di sistema denominati.

Vedi anche