c# - thread - ser notificado cuando todos los hilos del hilo de fondo estén terminados
threadpool.queueuserworkitem example c# (7)
Si no hay más de 64 subprocesos para esperar, puede usar el método WaitHandle.WaitAll como este:
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 configuren todos los ManualResetEvents; alternativamente, puede usar 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 luego determinar si el hilo está hecho.
Tengo un escenario cuando comienzo 3.10 hilos con ThreadPool. Cada hilo 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 terminado?
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.
¿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.
¿No podría darle a cada hilo un ManualResetEvent distinto y establecer el evento cuando termine? Luego, en el hilo principal puede esperar todos los eventos pasados.
La solución de Marc es mejor si solo quieres saber cuándo terminaron todos los trabajos, y no necesitas más información que esa (como parece ser 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, tal como está escrito, siempre devuelve verdadero, pero lo he escrito así para recordarle que algunas sobrecargas toman un tiempo de espera como argumento.
No hay una forma incorporada de hacerlo en este momento: creo que 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.
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 tienes el último hilo (es decir, cuando llega a cero) plantea 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 ver las opciones de Parallel.ForEach
aquí ... que podría ser otra forma de hacer todo como un bloque. Sin tener que mirarlos todos manualmente.
Pruebe 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();
}
}
}
Se pueden usar varios PoolGuard de diferentes maneras para rastrear cuándo terminaron los hilos, y maneja los hilos que no han comenzado cuando el grupo ya está cerrado.