2010-03-25 16 views

Respuesta

18

Tendrá que hacer un seguimiento de esto usted mismo.

Una opción para esto es utilizar un contador y un evento de reinicio:

int toProcess = 10; 
using(ManualResetEvent resetEvent = new ManualResetEvent(false)) 
{ 
    var list = new List<int>(); 
    for(int i=0;i<10;i++) list.Add(i); 
    for(int i=0;i<10;i++) 
    { 
     ThreadPool.QueueUserWorkItem(
      new WaitCallback(x => { 
       Console.WriteLine(x); 
       // Safely decrement the counter 
       if (Interlocked.Decrement(ref toProcess)==0) 
       resetEvent.Set(); 

      }),list[i]); 
    } 

    resetEvent.WaitOne(); 
} 
// When the code reaches here, the 10 threads will be done 
Console.WriteLine("Done"); 
+0

lol. Creo que 'hecho' es mejor nombre que 'mre' aunque –

+0

@Remus: ¿eso es mejor? –

+0

Por supuesto, hace toda la diferencia;) –

8

No estoy seguro de si ThreadPool expone dicha funcionalidad, pero se puede utilizar espere asas y por la forma en la iteración en dos ocasiones parece innecesario:

var events = new ManualResetEvent[10]; 
var list = new List<int>(); 
for (int i = 0; i < 10; i++) 
{ 
    list.Add(i); 
    events[i] = new ManualResetEvent(false); 
    int j = i; 
    ThreadPool.QueueUserWorkItem(x => { 
     Console.WriteLine(x); 
     events[j].Set(); 
    }, list[i]); 
} 
WaitHandle.WaitAll(events); 
+0

+1 - la misma solución que publiqué; solo fuiste más rápido y te presentaron un código un poco más elegante. –

+0

OK, también fuiste más rápido que yo. – Timores

+8

Solo una advertencia con esto ... WaitHandle.WaitTodos fallarán si tiene más de 64 elementos en un hilo STA ... –

1

El grupo de subprocesos no indica cuándo el subproceso ha terminado de ejecutarse, por lo que el elemento de trabajo debe hacerlo por sí mismo. Cambié el código de esta manera:

var list = new List<int>(); 
    ManualResetEvent[] handles = new ManualResetEvent[10]; 
    for (int i = 0; i < 10; i++) { 
     list.Add(i); 
     handles[i] = new ManualResetEvent(false); 
    } 
    for (int i = 0; i < 10; i++) { 
     ThreadPool.QueueUserWorkItem(
     new WaitCallback(x => 
     { 
      Console.WriteLine(x); 
      handles[(int) x].Set(); 
     }), list[i]); 
    } 

    WaitHandle.WaitAll(handles); 
0

Así es como lo haría.

class Program 
{ 
    static void Main(string[] args) 
    { 
     var items = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 
     using (var countdown = new Countdown(items.Length)) 
     { 
      foreach (var item in items) 
      { 
       ThreadPool.QueueUserWorkItem(o => 
       { 
        Thread.SpinWait(100000000); 
        Console.WriteLine("Thread Done!"); 
        countdown.Signal(); 
       }); 
      } 
      countdown.Wait(); 
     } 
     Console.WriteLine("Job Done!"); 
     Console.ReadKey(); 
    } 

    public class Countdown : IDisposable 
    { 
     private readonly ManualResetEvent done; 
     private readonly int total; 
     private volatile int current; 

     public Countdown(int total) 
     { 
      this.total = total; 
      current = total; 
      done = new ManualResetEvent(false); 
     } 

     public void Signal() 
     { 
      lock (done) 
      { 
       if (current > 0 && --current == 0) 
        done.Set(); 
      } 
     } 

     public void Wait() 
     { 
      done.WaitOne(); 
     } 

     public void Dispose() 
     { 
      done.Dispose(); 
     } 
    } 
} 
+0

Esto sería más eficaz con Interlocked.Decrement en lugar de con un candado. Ver mi respuesta para el código relevante. –

+0

Llamar hecho. Deshacer() en la cuenta regresiva es incorrecto, da "inaccesible debido a su nivel de protección". Debería hacerse. Cerrar() como Cerrar y Eliminar son equivalentes. – AceJordin

11

En .NET Framework 4+ utilizan la práctica de clase System.Threading.CountdownEvent:

const int threadCount = 10; 
var list = new List<int>(threadCount); 
for (var i = 0; i < threadCount; i++) list.Add(i); 

using (var countdownEvent = new CountdownEvent(threadCount)) 
{ 
    for (var i = 0; i < threadCount; i++) 
     ThreadPool.QueueUserWorkItem(
      x => 
      { 
       Console.WriteLine(x); 
       countdownEvent.Signal(); 
      }, list[i]); 

    countdownEvent.Wait(); 
} 
Console.WriteLine("done"); 
Cuestiones relacionadas