2010-08-06 19 views
5

Estoy escribiendo un servicio de Windows que usa ThreadPool.QueueUserWorkItem(). Cada hilo es una tarea de corta duración.¿Cómo puedo lograr ThreadPool.Join?

Cuando se detiene el servicio, necesito asegurarme de que todos los hilos que se están ejecutando actualmente se completen. ¿Hay alguna forma de esperar hasta que la cola se borre sola?

Respuesta

8

Puede crear un evento (por ejemplo, ManualResetEvent) en cada hilo y mantenerlo en una lista sincronizada (utilizando el constructo lock). Establezca el evento o elimínelo de la lista cuando la tarea finalice.

Cuando desee unirse, puede usar WaitHandle.WaitAll (MSDN documentation) para esperar a que se sigan todos los eventos.

Es un truco, pero no veo cómo reducirlo a algo más simple.


Editar: adicionalmente, podría asegurarse de que no se publiquen nuevos eventos, luego espere unos segundos. Si son de corta duración, no tendrás ningún problema. Incluso más simple, pero más hacky.

Por último, si es solo un corto período de tiempo, el servicio no se cerrará hasta que todos los hilos hayan desaparecido (a menos que sean hilos de fondo); entonces, si es poco tiempo, al administrador de control de servicios no le importará ni un segundo, puede dejar que caduque, según mi experiencia.

+0

Deben ser efímeros, pero para mi caso de uso, necesito más seguridad que "debería funcionar". Estoy pensando en usar un contador de subprocesos entrelazados para rastrear la cantidad de subprocesos en el grupo y esperar que iguale el cero. – recursive

+0

En realidad, me gusta su primera sugerencia mejor que el contador. – recursive

3

El patrón estándar para hacer esto es usar un contador que contiene el número de elementos de trabajo pendientes y uno ManualResetEvent que se señala cuando el contador llega a cero. En general, esto es mejor que usar WaitHandle para cada elemento de trabajo, ya que no se escala muy bien cuando hay muchos elementos de trabajo simultáneos. Además, algunos de los métodos estáticos de WaitHandle solo aceptan un máximo de 64 instancias de todos modos.

// Initialize to 1 because we are going to treat the current thread as 
// a work item as well. This is to avoid a race that could occur when 
// one work item gets queued and completed before the next work item 
// is queued. 
int count = 1; 
var finished = new ManualResetEvent(false); 
try 
{ 
    while (...) 
    { 
    Interlocked.Increment(ref counter); 
    ThreadPool.QueueUserWorkItem( 
     delegate(object state) 
     { 
     try 
     { 
      // Your task goes here. 
     } 
     finally 
     { 
      // Decrement the counter to indicate the work item is done. 
      if (Interlocked.Decrement(ref count) == 0) 
      { 
      finished.Set(); 
      } 
     } 
     }); 
    } 
} 
finally 
{ 
    // Decrement the counter to indicate the queueing thread is done. 
    if (Interlocked.Decrement(ref count) == 0) 
    { 
    finished.Set(); 
    } 
} 
finished.WaitOne(); 
Cuestiones relacionadas