¿Cómo esperar a que el hilo termine con .NET?

Nunca he usado threading antes en C # donde necesito tener dos hilos, así como el hilo principal de UI. Básicamente, tengo lo siguiente.

public void StartTheActions() { //Starting thread 1.... Thread t1 = new Thread(new ThreadStart(action1)); t1.Start(); // Now, I want for the main thread (which is calling `StartTheActions` method) // to wait for `t1` to finish. I've created an event in `action1` for this. // The I wish `t2` to start... Thread t2 = new Thread(new ThreadStart(action2)); t2.Start(); } 

Entonces, esencialmente, mi pregunta es cómo hacer que un hilo espere a que termine otro. ¿Cuál es la mejor manera de hacer esto?

Puedo ver 5 opciones disponibles:

1. Subproceso. Únete

Como con la respuesta de Mitch. Pero esto bloqueará su subproceso de interfaz de usuario, sin embargo, obtendrá un tiempo de espera integrado para usted.


2. Use un WaitHandle

ManualResetEvent es un WaitHandle como sugirió jrista.

Una cosa a tener en cuenta es que si desea esperar múltiples hilos, WaitHandle.WaitAll() no funcionará de manera predeterminada, ya que necesita un hilo MTA. Puede evitar esto marcando su método Main() con MTAThread ; sin embargo, esto bloquea la bomba de mensajes y no se recomienda por lo que he leído.


3. Dispara un evento

Vea esta página de Jon Skeet sobre eventos y multi-threading, es posible que un evento se vuelva a no incluir entre if y EventName(this,EventArgs.Empty) ; me ha pasado antes.

(Esperemos que compilen, no lo he intentado)

 public class Form1 : Form { int _count; void ButtonClick(object sender, EventArgs e) { ThreadWorker worker = new ThreadWorker(); worker.ThreadDone += HandleThreadDone; Thread thread1 = new Thread(worker.Run); thread1.Start(); _count = 1; } void HandleThreadDone(object sender, EventArgs e) { // You should get the idea this is just an example if (_count == 1) { ThreadWorker worker = new ThreadWorker(); worker.ThreadDone += HandleThreadDone; Thread thread2 = new Thread(worker.Run); thread2.Start(); _count++; } } class ThreadWorker { public event EventHandler ThreadDone; public void Run() { // Do a task if (ThreadDone != null) ThreadDone(this, EventArgs.Empty); } } } 

4. Usa un delegado

 public class Form1 : Form { int _count; void ButtonClick(object sender, EventArgs e) { ThreadWorker worker = new ThreadWorker(); Thread thread1 = new Thread(worker.Run); thread1.Start(HandleThreadDone); _count = 1; } void HandleThreadDone() { // As before - just a simple example if (_count == 1) { ThreadWorker worker = new ThreadWorker(); Thread thread2 = new Thread(worker.Run); thread2.Start(HandleThreadDone); _count++; } } class ThreadWorker { // Switch to your favourite Action or Func public void Run(object state) { // Do a task Action completeAction = (Action)state; completeAction.Invoke(); } } } 

Si usa el método _count, podría ser una idea (para estar seguro) incrementarlo usando

Interlocked.Increment(ref _count)

Me interesaría saber la diferencia entre usar delegates y eventos para la notificación de subprocesos, la única diferencia que sé es que los eventos se llaman sincrónicamente.


5. Hazlo asincrónicamente en vez

La respuesta a esta pregunta tiene una descripción muy clara de sus opciones con este método.


Delegado / Eventos en el hilo equivocado

La forma de hacer evento / delegado significará que el método del controlador de eventos está en thread1 / thread2, no en el hilo principal de UI , por lo que deberá volver a la derecha en la parte superior de los métodos HandleThreadDone:

 // Delegate example if (InvokeRequired) { Invoke(new Action(HandleThreadDone)); return; } 

Añadir

 t1.Join(); // Wait until thread t1 finishes 

después de que lo inicie, pero eso no logrará mucho ya que es esencialmente el mismo resultado que correr en el hilo principal.

Recomiendo encarecidamente leer el e-book Threading in C # de Joe Albahari, si quieres entender cómo se enhebra en .NET.

Las dos respuestas anteriores son excelentes y funcionarán para escenarios simples. Sin embargo, hay otras maneras de sincronizar los hilos. Lo siguiente también funcionará:

 public void StartTheActions() { ManualResetEvent syncEvent = new ManualResetEvent(false); Thread t1 = new Thread( () => { // Do some work... syncEvent.Set(); } ); t1.Start(); Thread t2 = new Thread( () => { syncEvent.WaitOne(); // Do some work... } ); t2.Start(); } 

ManualResetEvent es uno de los varios WaitHandle que el .NET framework tiene para ofrecer. Pueden proporcionar capacidades de sincronización de subprocesos mucho más ricas que las herramientas simples pero muy comunes como lock () / Monitor, Thread.Join, etc. También se pueden usar para sincronizar más de dos subprocesos, lo que permite escenarios complejos como un subproceso ‘master’ que coordina múltiples subprocesos ‘secundarios’, múltiples procesos simultáneos que dependen de varias etapas de sincronización entre ellos, etc.

Si usa .NET 4, esta muestra puede ayudarlo a:

 class Program { static void Main(string[] args) { Task task1 = Task.Factory.StartNew(() => doStuff()); Task task2 = Task.Factory.StartNew(() => doStuff()); Task task3 = Task.Factory.StartNew(() => doStuff()); Task.WaitAll(task1, task2, task3); Console.WriteLine("All threads complete"); } static void doStuff() { //do stuff here } } 

desde: https://stackoverflow.com/a/4190969/1676736

Desea el método Thread.Join() o una de sus sobrecargas .

Me gustaría que su hilo principal pase un método de callback a su primer hilo, y cuando lo haga, invocará el método de callback en el hilo principal, que puede ejecutar el segundo hilo. Esto evita que su hilo principal se cuelgue mientras está esperando un Join o Waithandle. Pasar los métodos como delegates es una cosa útil para aprender con C # de todos modos.

Publicando para tal vez ayudar a otros, pasó bastante tiempo buscando una solución como la que se me ocurrió. Así que tomé un enfoque diferente. Hay una opción de contador arriba, simplemente la apliqué de forma un poco diferente. Estaba generando numerosos hilos e incrementé un contador y decrementé un contador cuando un hilo comenzaba y se detenía. Luego, en el método principal, quería detenerme y esperar a que terminaran los hilos, lo hice.

 while (threadCounter > 0) { Thread.Sleep(500); //Make it pause for half second so that we don't spin the cpu out of control. } 

Documentado en mi blog http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/

Prueba esto:

 List myThreads = new List(); foreach (Thread curThread in myThreads) { curThread.Start(); } foreach (Thread curThread in myThreads) { curThread.Join(); }