2008-12-11 12 views
10

Tengo un escenario cuando inicio 3..10 subprocesos con ThreadPool. Cada subproceso hace su trabajo y regresa a ThreadPool. ¿Cuáles son las posibles opciones que se notificarán en el hilo principal cuando todos los hilos de fondo hayan finalizado?se notificará cuando se hayan terminado todos los subprocesos de subprocesos de subprocesos

Actualmente estoy usando un método de cosecha propia con el incremento de una variable para cada uno de los hilos creados y disminuyéndolo cuando un hilo de fondo está a punto de terminar. Esto funciona bien, pero tenía curiosidad si hay mejores opciones.

Respuesta

11

Disminuir una variable (entre subprocesos) es un poco arriesgado a menos que se haga con Interlocked.Decrement, pero ese enfoque debería estar bien si tiene el último subproceso (es decir, cuando llega a cero) provocar un evento. Tenga en cuenta que debería estar en un bloque "final" para evitar perderlo en el caso de excepciones (además de que no desea eliminar el proceso).

En "Extensiones paralelas" (o con .NET 4.0), también puede consultar las opciones Parallel.ForEach aquí ... que podría ser otra forma de hacer todo como un bloque. Sin tener que mirarlos todos manualmente.

+0

Hola Marc. Lo estoy haciendo de manera segura, con enclavamiento o palabra clave de bloqueo. Funciona bien y de forma no bloqueante. Me preguntaba si hay elementos primitivos incorporados para hacerlo. Gracias. – Valentin

2

No hay una forma incorporada de hacerlo en este momento. Para mí, es uno de los mayores problemas al usar subprocesos de grupo.

Como dice Marc, este es el tipo de cosas que se arreglan en Parallel Extensions/.NET 4.0.

1

¿No podría darle a cada subproceso un ManualResetEvent distinto y establecer el evento cuando termine? Luego, en el hilo principal, puede esperar todos los eventos pasados.

0

La solución de Marc es la mejor si solo quiere saber cuándo terminaron todos los trabajos y no necesita más información que esa (como parece se tu caso).

Si quería un hilo para generar trabajos y otro hilo para recibir las notificaciones, podría usar WaitHandle. El código es mucho más largo.

int length = 10; 
    ManualResetEvent[] waits = new ManualResetEvent[length]; 
    for (int i = 0; i < length; i++) { 
     waits[i] = new ManualResetEvent(false); 
     ThreadPool.QueueUserWorkItem((obj) => { 
      try { 

      } finally { 
       waits[i].Set(); 
      } 
     }); 
    } 

    for (int i = 0; i < length; i++) { 
     if (!waits[i].WaitOne()) 
      break; 
    } 

El método WaitOne, como está escrito, siempre devuelve cierto, pero lo he escrito así para que se recuerde que algunas sobrecargas toman un tiempo de espera como argumento.

3

Si sus no más de 64 hilos que esperar en, puede utilizar el método WaitHandle.WaitAll así:

List<WaitHandle> events = new List<WaitHandle>(); 
for (int i = 0; i < 64; i++) 
{ 
    ManualResetEvent mre = new ManualResetEvent(false); 
    ThreadPool.QueueUserWorkItem(
     delegate(object o) 
     { 
      Thread.Sleep(TimeSpan.FromMinutes(1)); 
      ((ManualResetEvent)o).Set(); 
     },mre); 
    events.Add(mre); 
} 
WaitHandle.WaitAll(events.ToArray()); 

La ejecución esperará hasta que se fijan todos los ManualResetEvents, de forma alternativa, puede utilizar el método WaitAny .

Los métodos WaitAny y WaitAll bloquearán la ejecución, pero simplemente puede usar la lista, o un diccionario de ManualResetEvents vinculado a la tarea que se engendra para determinar luego si el hilo está hecho.

+1

Este método funciona muy bien siempre que Main() no sea un Single Thread Apartment ([STAThread]). –

+0

¿Por qué necesita ser "más de 64 subprocesos para esperar"? – jp2code

+0

Guau, esa pregunta golpea ... la respuesta es más de 4 años ... como recuerdo, hay una limitación en el WaitHandle.WaitTodo eso arroja una excepción si la lista de identificadores tiene más de 64 elementos. Tal vez esto esté desactualizado hoy. –

0

¿Qué pasa con el uso de Semaphore, y establecer un límite tanto como su grupo de subprocesos. Disponga de un método para buscar un semáforo, que se ejecutará cuando inicie el hilo, lo liberará cuando finalice el hilo y se generará un evento si ha ocupado todo el semáforo.

4

Prueba esto: https://bitbucket.org/nevdelap/poolguard

using (var poolGuard = new PoolGuard()) 
{ 
    for (int i = 0; i < ... 
    { 
     ThreadPool.QueueUserWorkItem(ChildThread, poolGuard); 
    } 
    // Do stuff. 
    poolGuard.WaitOne(); 
    // Do stuff that required the child threads to have ended. 

void ChildThread(object state) 
{ 
    var poolGuard = state as PoolGuard; 
    if (poolGuard.TryEnter()) 
    { 
     try 
     { 
      // Do stuff. 
     } 
     finally 
     { 
      poolGuard.Exit(); 
     } 
    } 
} 

múltiples PoolGuards se pueden utilizar de diferentes maneras para realizar un seguimiento de las discusiones cuando han terminado, y se ocupa de temas que no han comenzado cuando la piscina ya está cerrado.

+0

El artículo ya no está disponible. ¿Qué es la clase PoolGuard? podrías copiar el código completo? – Niloofar

Cuestiones relacionadas