EventWaitHandle
La clase EventWaitHandle permite que los subprocesos se comuniquen entre sí mediante señalización y espera de señales. Los identificadores de espera de eventos (también denominados simplemente como eventos) son identificadores de espera que se pueden señalar con el fin de liberar uno o varios subprocesos en espera. Cuando se envía una señal, se restablece un identificador de espera de evento de forma manual o automática. La clase EventWaitHandle puede representar cualquier identificador de espera de evento local (evento local) o identificador de espera de evento del sistema con nombre (denominado evento o evento del sistema y visible para todos los procesos).
Nota
Los identificadores de espera de evento no son eventos de .NET. No existen delegados ni controladores de eventos implicados. Se utiliza el término "evento" para describirlos porque tradicionalmente se ha hecho referencia a ellos como eventos del sistema operativo y porque el acto de señalar el indicador de espera indica a los subprocesos en espera que se ha producido un evento.
Ambos identificadores de espera de evento local y con nombre usan objetos de sincronización del sistema, protegidos por contenedores SafeWaitHandle para garantizar la liberación de los recursos. Puede usar el método Dispose para liberar los recursos inmediatamente cuando haya terminado de utilizar el objeto.
Identificadores de espera de evento que se restablecen automáticamente
Cree un evento de restablecimiento automático definiendo EventResetMode.AutoReset al crear el objeto EventWaitHandle. Como su nombre implica, este evento de sincronización se restablece automáticamente cuando se señala, después de liberar un único subproceso en espera. Señale el evento mediante una llamada a su método Set.
Los eventos de restablecimiento automático se utilizan normalmente para proporcionar acceso exclusivo a un recurso a un único subproceso cada vez. Un subproceso solicita el recurso mediante una llamada al método WaitOne. Si ningún otro subproceso contiene el identificador de espera, el método devuelve true
y el subproceso que realiza la llamada tiene el control del recurso.
Importante
Al igual que con todos los mecanismos de sincronización, debe asegurarse de que todas las rutas de acceso de código esperen al identificador de espera adecuado antes de obtener acceso a un recurso protegido. La sincronización de subprocesos es cooperativa.
Si un evento de restablecimiento automático se señaliza cuando no hay ningún subproceso en espera, permanece señalado hasta que un subproceso intenta esperar en él. El evento libera el subproceso y lo restablece inmediatamente, bloqueando los subprocesos subsiguientes.
Identificadores de espera de evento que se restablecen manualmente
Cree un evento de restablecimiento manual definiendo EventResetMode.ManualReset al crear el objeto EventWaitHandle. Como su nombre implica, este evento de sincronización debe restablecerse manualmente después de que se haya señalado. Hasta que se restablezca, mediante una llamada a su método Reset, los subprocesos que esperan en el identificador de evento se ejecutarán inmediatamente, sin bloquearse.
Un evento de restablecimiento manual funciona como la puerta de un establo. Si no se señala un evento, los subprocesos que esperan en él se bloquean, como los caballos de un establo. Cuando se señala el evento, mediante una llamada a su método Set, todos los subprocesos en espera pueden continuar libremente. El evento permanece señalado hasta que se llama a su método Reset. Esto hace que el evento de restablecimiento manual sea una forma ideal de retener subprocesos que necesitan esperar hasta que un subproceso finaliza una tarea.
Como los caballos cuando dejan un establo, lleva tiempo que el sistema operativo programe los subprocesos liberados y reanudar su ejecución. Si se llama al método Reset antes de que se reanude la ejecución de los subprocesos, los subprocesos restantes se vuelven a bloquear. Los subprocesos que se reanudan y los subprocesos que se bloquean es una cuestión que depende de factores aleatorios, como la carga del sistema, el número de subprocesos que esperan al programador, etc. Esto no plantea ningún problema si el subproceso que señala el evento termina después de la señalización, que es el patrón de uso más común. Si desea que el subproceso que señaló el evento inicie una nueva tarea después de que todos los subprocesos en espera se hayan reanudado, debe bloquearlo hasta que todos los subprocesos en espera se hayan reanudado. En caso contrario, se produce una condición de carrera y el comportamiento del código es imprevisible.
Características comunes de eventos automáticos y manuales
Normalmente, uno o varios subprocesos se bloquean en EventWaitHandle hasta que un subproceso desbloqueado llama al método Set, lo que libera uno de los subprocesos en espera (en el caso de los eventos de restablecimiento automático) o todos ellos (en el caso de eventos de restablecimiento manual). Un subproceso puede señalar EventWaitHandle y, a continuación, bloquearse ahí, como una operación atómica, mediante una llamada al método estático WaitHandle.SignalAndWait.
Los objetos EventWaitHandle pueden usarse con los métodos estáticos WaitHandle.WaitAll y WaitHandle.WaitAny. Dado que las clases EventWaitHandle y Mutex se derivan de WaitHandle, puede usar las dos con estos métodos.
Eventos con nombre
El sistema operativo Windows permite que los identificadores de espera de evento tengan nombres. Un evento con nombre abarca todo el sistema. Es decir, una vez creado el evento con nombre, es visible para todos los subprocesos de todos los procesos. Por lo tanto, los eventos con nombre pueden utilizarse para sincronizar tanto las actividades de procesos como las de subprocesos.
Puede crear un objeto EventWaitHandle que represente un evento de sistema con nombre mediante el uso de uno de los constructores que especifica un nombre de evento.
Nota
Dado que los eventos con nombre abarcan todo el sistema, es posible tener varios objetos EventWaitHandle que representan el mismo evento con nombre. Cada vez que llama a un constructor o al método OpenExisting, se crea un objeto EventWaitHandle. Si se especifica el mismo nombre repetidamente, se crean varios objetos que representan el mismo evento con nombre.
Se recomienda actuar con precaución en el uso de los eventos con nombre. Dado que abarcan todo el sistema, otro proceso que utilice el mismo nombre puede bloquear los subprocesos inesperadamente. Si hubiera código malintencionado ejecutándose en el mismo equipo, dicho código podría utilizar esto como base de un ataque de denegación de servicio.
Utilice la seguridad de control de acceso para proteger un objeto EventWaitHandle que representa un evento con nombre, a poder ser mediante un constructor que especifique un objeto EventWaitHandleSecurity. También puede aplicar la seguridad de control de acceso mediante el método SetAccessControl, pero esto deja una ventana de vulnerabilidad entre el momento en que se crea el identificador de espera de evento y el momento en que se protege. Proteger los eventos con seguridad de control de acceso ayuda a evitar ataques malintencionados, pero no soluciona el problema de conflictos involuntarios de nombres.
Nota
A diferencia de la clase EventWaitHandle, las clases derivadas AutoResetEvent y ManualResetEvent pueden representar solo identificadores de espera locales. No pueden representar eventos del sistema con nombre.