2010-09-23 24 views
6

Estoy tratando de utilizar ThreadPool.RegisterWaitForSingleObject añadir un temporizador para un conjunto de hilos. Creo 9 hilos y trato de darles a cada uno la misma oportunidad de operación ya que en este momento parece haber un poco de hambre pasando solo si los agrego al grupo de subprocesos. También estoy tratando de implementar un evento de reinicio manual ya que quiero que los 9 hilos salgan antes de continuar.La forma correcta para implementar ThreadPool.RegisterWaitForSingleObject

¿Cuál es la mejor manera de asegurar que cada subproceso en el grupo de subprocesos tenga la misma oportunidad de ejecución ya que la función a la que llamo tiene un bucle y parece que cada subproceso (o el que se ejecuta primero) se queda atascado en él y los otros no tienen la oportunidad de correr.

resetEvents = new ManualResetEvent[table_seats]; 
      //Spawn 9 threads 
      for (int i = 0; i < table_seats; i++) 
      { 
       resetEvents[i] = new ManualResetEvent(false); 
       //AutoResetEvent ev = new AutoResetEvent(false); 
       RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(autoEvent, ObserveSeat, (object)i, 100, false); 
      } 

      //wait for threads to exit 
      WaitHandle.WaitAll(resetEvents); 

Sin embargo, no importa si uso resetEvents [] o si parece que ninguno de ellos funciona correctamente. ¿Puedo implementar esto o estoy (probablemente) malinterpretando cómo deberían funcionar?

Gracias, R.

Respuesta

4

yo no usaría el RegisterWaitForSingleObject para este propósito. Los patrones que voy a describir aquí requieren la descarga Reactive Extensions ya que está utilizando .NET v3.5.

Primero, para esperar todos los elementos de trabajo del ThreadPool para completar el uso de la clase CountdownEvent. Esto es mucho más elegante y escalable que usar múltiples instancias ManualResetEvent. Además, el método WaitHandle.WaitAll está limitado a 64 identificadores.

var finished = new CountdownEvent(1); 
for (int i = 0; i < table_seats; i++) 
{ 
    finished.AddCount(); 
    ThreadPool.QueueUserWorkItem(ObserveSeat); 
    (state) => 
    { 
     try 
     { 
     ObserveSeat(state); 
     } 
     finally 
     { 
     finished.Signal(); 
     } 
    }, i); 
} 
finished.Signal(); 
finished.Wait(); 

En segundo lugar, usted podría intentar llamar Thread.Sleep(0) después de varias iteraciones del bucle para forzar un cambio de contexto para que los rendimientos actuales de hilo a otro. Si desea una estrategia de coordinación considerablemente más compleja, utilice la clase Barrier. Agregue otro parámetro a su función ObserveSeat que acepte este mecanismo de sincronización. Puede suministrarlo capturándolo en la expresión lambda en el código anterior.

public void ObserveSeat(object state, Barrier barrier) 
{ 
    barrier.AddParticipant(); 
    try 
    { 
    for (int i = 0; i < NUM_ITERATIONS; i++) 
    { 
     if (i % AMOUNT == 0) 
     { 
     // Let the other threads know we are done with this phase and wait 
     // for them to catch up. 
     barrier.SignalAndWait(); 
     } 
     // Perform your work here. 
    } 
    } 
    finally 
    { 
    barrier.RemoveParticipant(); 
    } 
} 

Tenga en cuenta que aunque este enfoque sin duda evitaría el problema de la inanición, podría limitar el rendimiento de los hilos. Llamar al SignalAndWait en exceso puede provocar una gran cantidad de cambios de contexto innecesarios, pero llamarlo demasiado poco puede causar una gran cantidad de espera innecesaria. Probablemente tendrías que sintonizar AMOUNT para obtener el equilibrio óptimo de rendimiento y la inanición. Sospecho que podría haber una forma simple de hacer la afinación de forma dinámica.

+1

Gracias, no he tenido tiempo de probar esto todavía, pero gracias Brian por la respuesta. – flavour404

Cuestiones relacionadas