2008-11-04 42 views
59

Me estoy encontrando con un patrón común en el código que estoy escribiendo, donde necesito esperar a que se completen todos los hilos de un grupo, con un tiempo de espera agotado. Se supone que el tiempo de espera es el tiempo requerido para todos los hilos para completar, simplemente haciendo thread.Join (timeout) para cada thread no funcionará, ya que el posible tiempo de espera es entonces timeout * numThreads.C#: Esperando a que terminen todos los hilos

Ahora mismo algo como lo siguiente:

var threadFinishEvents = new List<EventWaitHandle>(); 

foreach (DataObject data in dataList) 
{ 
    // Create local variables for the thread delegate 
    var threadFinish = new EventWaitHandle(false, EventResetMode.ManualReset); 
    threadFinishEvents.Add(threadFinish); 

    var localData = (DataObject) data.Clone(); 
    var thread = new Thread(
     delegate() 
     { 
      DoThreadStuff(localData); 
      threadFinish.Set(); 
     } 
    ); 
    thread.Start(); 
} 

Mutex.WaitAll(threadFinishEvents.ToArray(), timeout); 

Sin embargo, parece que no debería haber un lenguaje más simple para este tipo de cosas.

+0

cualquier solución final con el código fuente completo de trabajo? tal vez una muestra más compleja para notificar errores en cada hilo y después de WaitAll muestra un resumen? – Kiquenet

+0

Eso es lo que haría, no puedo pensar en una forma más sencilla de hacerlo. – Guy

Respuesta

25

Sigo pensando que usar Join es más simple. Registrar el tiempo de finalización prevista (como ahora + tiempo de espera) y, a continuación, en un bucle, hacer

if(!thread.Join(End-now)) 
    throw new NotFinishedInTime(); 
8

Fuera de mi cabeza, ¿por qué no acabas de Thread.Join (tiempo de espera) y quitas el tiempo que tardó en unirse desde el tiempo de espera total?

// pseudo-c#: 

TimeSpan timeout = timeoutPerThread * threads.Count(); 

foreach (Thread thread in threads) 
{ 
    DateTime start = DateTime.Now; 

    if (!thread.Join(timeout)) 
     throw new TimeoutException(); 

    timeout -= (DateTime.Now - start); 
} 

Editar: código es ahora menos de pseudo. no entiendo por qué modificaría una respuesta -2 cuando la respuesta modificada +4 es exactamente la misma, solo que menos detallada.

+0

Eso es incorrecto: se da el tiempo de espera completo para todos los hilos, cualquier hilo (incluido el primero que se espera) puede consumir el tiempo de espera completo. Su código no verifica el resultado de la unión. –

+3

En primer lugar, se trata de un pseudocódigo y, como no es código real, es incompleto. Supuse que un tiempo igual o menor a cero arrojará o algo así. En segundo lugar, si el primer hilo consume todo el tiempo, la operación debería expirar en conjunto. Esto es correcto según la pregunta. –

6

esto puede no ser una opción para usted, pero si se puede usar la extensión paralela para .NET entonces se podría utilizar Task s en lugar de hilos sin procesar y luego use Task.WaitAll() para esperar a que se completen.

1

Estaba tratando de descubrir cómo hacer esto, pero no pude obtener ninguna respuesta de Google. Sé que esto es un hilo viejo, pero aquí fue mi solución:

Utilice la siguiente clase:

class ThreadWaiter 
    { 
     private int _numThreads = 0; 
     private int _spinTime; 

     public ThreadWaiter(int SpinTime) 
     { 
      this._spinTime = SpinTime; 
     } 

     public void AddThreads(int numThreads) 
     { 
      _numThreads += numThreads; 
     } 

     public void RemoveThread() 
     { 
      if (_numThreads > 0) 
      { 
       _numThreads--; 
      } 
     } 

     public void Wait() 
     { 
      while (_numThreads != 0) 
      { 
       System.Threading.Thread.Sleep(_spinTime); 
      } 
     } 
    } 
  1. Addthreads de llamadas (int numThreads) antes de ejecutar un hilo (s).
  2. Llamar a RemoveThread() después de que cada uno haya completado.
  3. Uso de espera() en el punto que desea esperar a que todos los hilos para completar antes de continuar
+0

publicarlo como una pregunta separada – xbonez

9

Dado que la cuestión empujaron voy a seguir adelante y publicar mi solución.

using (var finished = new CountdownEvent(1)) 
{ 
    for (DataObject data in dataList) 
    { 
    finished.AddCount(); 
    var localData = (DataObject)data.Clone(); 
    var thread = new Thread( 
     delegate() 
     { 
      try 
      { 
      DoThreadStuff(localData); 
      threadFinish.Set(); 
      } 
      finally 
      { 
      finished.Signal(); 
      } 
     } 
    ); 
    thread.Start(); 
    } 
    finished.Signal(); 
    finished.Wait(YOUR_TIMEOUT); 
} 
20

Con .NET 4.0 me parece System.Threading.Tasks mucho más fácil trabajar con ellos. Aquí está el ciclo spin-wait que funciona de manera confiable para mí. Bloquea el hilo principal hasta que todas las tareas se completen. También hay Task.WaitAll, pero eso no siempre me ha funcionado.

 for (int i = 0; i < N; i++) 
     { 
      tasks[i] = Task.Factory.StartNew(() => 
      {    
       DoThreadStuff(localData); 
      }); 
     } 
     while (tasks.Any(t => !t.IsCompleted)) { } //spin wait 
+33

Desea 'Task.WaitAll (tasks)' en lugar de ese giro. – ladenedge

+13

@ T.Webster: ¿qué quiere decir con que 'Task.WaitAll' no siempre funcionó para usted? ¿Estás sugiriendo que este método tiene errores? ¿Puedes dar ejemplos? Siempre me funciona. Por otro lado, el giro que sugieres es bastante caro. Estás martillando tu CPU. 'Task.WaitAll' es una solución mucho mejor. –

+2

Si usa esta opción, sugeriría al menos poner 'Thread.Yield()' en el cuerpo while. –

0

Posible solución:

var tasks = dataList 
    .Select(data => Task.Factory.StartNew(arg => DoThreadStuff(data), TaskContinuationOptions.LongRunning | TaskContinuationOptions.PreferFairness)) 
    .ToArray(); 

var timeout = TimeSpan.FromMinutes(1); 
Task.WaitAll(tasks, timeout); 

Suponiendo DataList es la lista de elementos y cada artículo debe ser procesada en un hilo separado.

+0

muestra más compleja para notificar errores en cada hilo y después de WaitAll muestra un resumen? – Kiquenet

1

Leí el libro C# 4.0: La referencia completa de Herbert Schildt. Usa el autor se unen para dar una solución:

class MyThread 
    { 
     public int Count; 
     public Thread Thrd; 
     public MyThread(string name) 
     { 
      Count = 0; 
      Thrd = new Thread(this.Run); 
      Thrd.Name = name; 
      Thrd.Start(); 
     } 
     // Entry point of thread. 
     void Run() 
     { 
      Console.WriteLine(Thrd.Name + " starting."); 
      do 
      { 
       Thread.Sleep(500); 
       Console.WriteLine("In " + Thrd.Name + 
       ", Count is " + Count); 
       Count++; 
      } while (Count < 10); 
      Console.WriteLine(Thrd.Name + " terminating."); 
     } 
    } 
    // Use Join() to wait for threads to end. 
    class JoinThreads 
    { 
     static void Main() 
     { 
      Console.WriteLine("Main thread starting."); 
      // Construct three threads. 
      MyThread mt1 = new MyThread("Child #1"); 
      MyThread mt2 = new MyThread("Child #2"); 
      MyThread mt3 = new MyThread("Child #3"); 
      mt1.Thrd.Join(); 
      Console.WriteLine("Child #1 joined."); 
      mt2.Thrd.Join(); 
      Console.WriteLine("Child #2 joined."); 
      mt3.Thrd.Join(); 
      Console.WriteLine("Child #3 joined."); 
      Console.WriteLine("Main thread ending."); 
      Console.ReadKey(); 
     } 
    } 
7

Esto no responde a la pregunta (sin tiempo de espera), pero he hecho un método de extensión muy simple que esperar todos los hilos de una colección:

using System.Collections.Generic; 
using System.Threading; 
namespace Extensions 
{ 
    public static class ThreadExtension 
    { 
     public static void WaitAll(this IEnumerable<Thread> threads) 
     { 
      if(threads!=null) 
      { 
       foreach(Thread thread in threads) 
       { thread.Join(); } 
      } 
     } 
    } 
} 

Entonces sólo tiene que llamar:

List<Thread> threads=new List<Thread>(); 
//Add your threads to this collection 
threads.WaitAll(); 
+0

Buen trabajo !!!!!! – KyloRen

Cuestiones relacionadas