2010-12-19 21 views
44

Veo en línea que dice que uso myThread.Join(); cuando quiero bloquear mi hilo hasta que termina otro hilo. (Una de las cosas que no entiendo sobre esto es si tengo varios hilos).Multithreading: ¿Cuándo usaría un Join?

Pero, en general, simplemente no aparece cuando uso .Join() o una condición que es útil para. ¿Puede alguien por favor explicarme esto como si fuera un alumno de cuarto grado? Una explicación muy simple para entender recibirá mi voto de respuesta.

Respuesta

49

Digamos que desea iniciar algunos subprocesos de trabajo para llevar a cabo algún tipo de cálculo, y luego hacer algo después con todos los resultados.

List<Thread> workerThreads = new List<Thread>(); 
List<int> results = new List<int>(); 

for (int i = 0; i < 5; i++) { 
    Thread thread = new Thread(() => { 
     Thread.Sleep(new Random().Next(1000, 5000)); 
     lock (results) { 
      results.Add(new Random().Next(1, 10)); 
     } 
    }); 
    workerThreads.Add(thread); 
    thread.Start(); 
} 

// Wait for all the threads to finish so that the results list is populated. 
// If a thread is already finished when Join is called, Join will return immediately. 
foreach (Thread thread in workerThreads) { 
    thread.Join(); 
} 

Debug.WriteLine("Sum of results: " + results.Sum()); 

Oh, sí, y no utilice aleatoria de esa manera, sólo estaba tratando de escribir un ejemplo mínimo, de fácil comprensión. Al final no es realmente aleatorio si crea nuevas instancias aleatorias demasiado cercanas en el tiempo, ya que la semilla se basa en el reloj.

+1

Es necesario añadir algo así como 'int value = i;' en el foor-loop antes de iniciar un nuevo Thread. Porque 'yo' puede estar aumentando antes de que comience un hilo y la suma no será determinista. –

8

Unir es utilizado principalmente cuando necesita esperar que un hilo (o un grupo de ellos) termine antes de proceder con su código.

Por esta razón, también es particularmente útil cuando necesita recopilar resultados de una ejecución de subproceso.

Según el comentario de Arafangion a continuación, también es importante unir los hilos si necesita hacer algún código de limpieza/limpieza después de haber creado un hilo.

+1

hay que mencionar que esto puede ser importante mientras se limpia un proceso de preparación para la salida. – Arafangion

+0

@Arafangion: ¡correcto! – Lorenzo

15

En el siguiente fragmento de código, el hilo principal llama Join() que hace que se espere a que todas las discusiones generados para terminar:

static void Main() 
{ 
    Thread regularThread = new Thread(ThreadMethod); 
    regularThread.Start(); 

    Thread regularThread2 = new Thread(ThreadMethod2); 
    regularThread2.Start(); 

    // Wait for spawned threads to end. 
    regularThread.Join(); 
    Console.WriteLine("regularThread returned."); 

    regularThread2.Join(); 
    Console.WriteLine("regularThread2 returned."); 
} 

Tenga en cuenta que si también hace girar un hilo desde el grupo de subprocesos (usando QueueUserWorkItem por ejemplo), Join no esperaría ese hilo de fondo. Debería implementar algún otro mecanismo, como usar un AutoResetEvent.

Para una excelente introducción a enhebrar, recomiendo la lectura de Joe Albahari libre Threading in C#

+1

gracias por el enlace al libro electrónico gratuito y valiosa –

7

Este es un programa muy simple para demostrar el uso de Thread Join. Siga mis comentarios para una mejor comprensión. Escriba este programa tal como está.

using System; 
    using System.Threading; 


    namespace ThreadSample 
    { 
     class Program 
     { 
      static Thread thread1, thread2; 
      static int sum=0; 
      static void Main(string[] args) 
      { 
       start(); 
       Console.ReadKey(); 
      } 
      private static void Sample() { sum = sum + 1; } 
      private static void Sample2() { sum = sum + 10; } 

      private static void start() 
      {  
       thread1 = new Thread(new ThreadStart(Sample)); 
       thread2 = new Thread(new ThreadStart(Sample2)); 
       thread1.Start(); 
       thread2.Start(); 
      // thread1.Join(); 
      // thread2.Join(); 
       Console.WriteLine(sum); 
       Console.WriteLine(); 
      } 
     } 
} 

1.En primer lugar tiempo de ejecución ya que es (con comentarios): Luego resultado será 0 (valor inicial) o 1 (cuando hilo 1 acabado) o 10 (o hilo de acabado)

2.Run con eliminación comentario thread1.Join(): resultado debe ser siempre más de 1 .because thread1.Join() dispararon e hilo 1 debe ser terminado antes de obtener la suma.

3.Run con eliminar todos los comentarios: resultado debe ser siempre 11

0

Adición de un retraso de 300 ms en el método de la "muestra" y un retraso de 400 ms en "Sample2" de post de devopsEMK haría Más fácil de entender.

De esta manera se puede observar que al eliminar el comentario de "thread1.Join();" línea, el hilo principal espera a que se complete el "hilo1" y solo después de que se mueva.

0

Otro ejemplo, cuando el subproceso de trabajo digamos que lee de un flujo de entrada, mientras que el método de lectura puede correr para siempre y que desea evitar de alguna manera esto - mediante la aplicación de tiempo de espera utilizando otro hilo de vigilancia:

// worker thread 
var worker = new Thread(() => { 
    Trace.WriteLine("Reading from stream"); 

    // here is the critical area of thread, where the real stuff happens 
    // Sleep is just an example, simulating any real operation 
    Thread.Sleep(10000); 

    Trace.WriteLine("Reading finished"); 
}) { Name = "Worker" }; 
Trace.WriteLine("Starting worker thread..."); 
worker.Start(); 

// watchdog thread 
ThreadPool.QueueUserWorkItem((o) => { 
    var timeOut = 5000; 
    if (!worker.Join(timeOut)) 
    { 
     Trace.WriteLine("Killing worker thread after " + timeOut + " milliseconds!"); 
     worker.Abort(); 
    } 
});