2009-01-09 6 views
6

Tengo un caso de rearme manual en todo el sistema que se crea haciendo lo siguiente:notificar correctamente todos los oyentes de un sistema de amplia nombrada caso de rearme manual e inmediatamente después de restablecerlo

EventWaitHandle notifyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, notifyEventName, out createdEvent); 

Varios procesos de creación de este evento (por ejemplo, se comparte entre ellos). Se usa para notificar cuando algo se actualiza.

Me gustaría poder configurar este evento para que todos los procesos que esperan en él se señalicen y luego reiniciarlo inmediatamente para que las siguientes Esperas en el evento estén bloqueadas.

Si hago un

notifyEvent.Set(); 
notifyEvent.Reset(); 

A veces se notificará a todos los procesos de escucha.

si hago un

notifyEvent.Set(); 
Thread.Sleep(0); 
notifyEvent.Reset(); 

Más procesos recibir una notificación (I supone que esto sucedería ya que el programador tiene la oportunidad de correr).

Y si lo hago

notifyEvent.Set(); 
Thread.Sleep(100); 
notifyEvent.Reset(); 

Entonces todo parece funcionar bien y todos los procesos (por ejemplo, ~ 8) Se puede notificarse de forma coherente. No me gusta el uso de un "número mágico" para la llamada Sleep.

Hay una mejor manera de notificar a todos los oyentes de un evento del sistema operativo en otros procesos que un evento ha ocurrido para que todos los que escuchan en el momento de la notificación reciban la señal de evento y restablecer inmediatamente el evento para que cualquier otra persona que va a escuchar el evento bloqueará?

ACTUALIZACIÓN: Un semáforo no parece ser una buena opción aquí ya que el número de oyentes del evento puede variar con el tiempo. No se sabe de antemano cuántos oyentes habrá cuando se necesite notificar a un par.

Respuesta

2

Está utilizando la clase EventWaitHandle incorrectamente. Un evento de reinicio no se debe usar para señalizar múltiples hilos. En su lugar, debe crear un evento de reinicio para cada subproceso y luego, cuando esté listo, recorrer todos ellos y usar Set(). El hilo maestro no debería llamar al método Reset(). Cada hilo debe ser responsable de cerrar la puerta detrás de ellos, por así decirlo.

Aquí hay un ejemplo básico:

static class Program 
{ 
    static void Main() 
    { 
     List<ThreadState> states = new List<ThreadState>(); 
     ThreadState myState; 
     Thread myThread; 
     string data = ""; 

     for (int i = 0; i < 4; i++) 
     { 
      myThread = new Thread(Work); 
      myState = new ThreadState(); 
      myState.gate = new EventWaitHandle(false, EventResetMode.ManualReset); 
      myState.running = true; 
      myState.index = i + 1; 
      states.Add(myState); 
      myThread.Start(myState); 
     } 

     Console.WriteLine("Enter q to quit."); 

     while (data != "q") 
     { 
      data = Console.ReadLine(); 
      if (data != "q") 
       foreach (ThreadState state in states) 
        state.gate.Set(); 
     } 

     foreach (ThreadState state in states) 
     { 
      state.running = false; 
      state.gate.Set(); 
     } 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static void Work(Object param) 
    { 
     ThreadState state = (ThreadState)param; 
     while (state.running) 
     { 
      Console.WriteLine("Thread #" + state.index + " waiting..."); 
      state.gate.WaitOne(); 
      Console.WriteLine("Thread #" + state.index + " gate opened."); 
      state.gate.Reset(); 
      Console.WriteLine("Thread #" + state.index + " gate closed."); 
     } 
     Console.WriteLine("Thread #" + state.index + " terminating."); 
    } 

    private class ThreadState 
    { 
     public int index; 
     public EventWaitHandle gate; 
     public bool running; 
    } 
} 
+0

Aceptaré esta respuesta, pero parece que el sistema operativo aún debería permitir que funcione una mano :) –

0

Aquí está utilizando el tipo de sincronización incorrecto. En lugar de un evento, debe usar la clase Semaphore, con la cantidad de accesos simultáneos que desea permitir.

También es posible que desee tener dos semáforos, el segundo es para el código que activa el evento para verificar (que el código que responde al evento mantendrá bloqueos) en caso de que no desee tener dos eventos en rápida sucesión y tener una sección de código en las colas de otro evento.

+0

el notificador no tiene forma de saber cuántos accesos simultáneos habrá. Cualquiera de los procesos puede notificar cuándo ocurre este evento. Además, los procesos de escucha pueden conectarse y desconectarse en cualquier momento. –

+0

Es decir, cualquier proceso puede tener un hilo que notifique que se ha actualizado una condición. Por estas razones, parece que un semáforo es una buena opción (número desconocido de oyentes que pueden variar) –

3

que tenía el mismo problema y, sorprendentemente, no pude encontrar ninguna buena solución en la web para este flojo-enchufados y/o dispara y olvida/oyentes múltiple tipo de evento, por lo aquí es lo que se me ocurrió.

Nota de la solución con el tiempo de espera entre Set() y Reset() llamadas tiene también una cuestión de raza-Estado (más allá del hecho de que se basa en un valor de tiempo de espera arbitraria): si el editor muere entre estas llamadas, entonces todos los oyentes verán el evento como establecido para siempre (a menos que el editor vuelva a funcionar).

lo tanto, el requisito es:

  • hay un editor (aunque en realidad no es ejecutada en el código)
  • no puede haber cualquier número de oyentes (en el mismo proceso o en otros procesos), entre 0 y N (N se fija una vez que se compilan los binarios).
  • oyentes pueden entrar y salir cuando quieran sin perturbar el editor
  • editor puede entrar y salir cuando quiera sin molestar a los oyentes

El truco es utilizar eventos de reposición automática, ya que no tienen raza problemas de condición, pero define uno por oyente. No sabemos el número de oyentes de antemano, pero podemos fijar un número máximo de oyentes ('N' descrito anteriormente):

const int MAX_EVENT_LISTENERS = 10; 
const string EVENT_NAME = "myEvent_"; 

Aquí es el código del editor para generar el evento a todos los oyentes potenciales:

public static void RaiseEvent() 
{ 
    for (int i = 0; i < MAX_EVENT_LISTENERS; i++) 
    { 
     EventWaitHandle evt; 
     if (EventWaitHandle.TryOpenExisting(EVENT_NAME + i, out evt)) 
     { 
      evt.Set(); 
      evt.Dispose(); 
     } 
    } 
} 

Aquí está el código para un detector para ser notificado del evento:

... 
EventWaitHandle evt = GetEvent(); 
do 
{ 
    bool b = evt.WaitOne(); 
    // event was set! 
} 
while (true); 
.... 

// create our own event that no other listener has 
public static EventWaitHandle GetEvent() 
{ 
    for (int i = 0; i < MAX_EVENT_LISTENERS; i++) 
    { 
     bool createdNew; 
     EventWaitHandle evt = new EventWaitHandle(false, EventResetMode.AutoReset, EVENT_NAME + i, out createdNew); 
     if (createdNew) 
      return evt; 

     evt.Dispose(); 
    } 
    throw new Exception("Increase MAX_EVENT_LISTENERS"); 
} 
Cuestiones relacionadas