2010-12-19 24 views
44

Widzę w Internecie, że jest napisane, że używam myThread.Join();, kiedy chcę zablokować wątek, aż skończy się inny wątek. (Jedną z rzeczy, których nie rozumiem, jest to, że jeśli mam wiele wątków).Wielowątkowość: Kiedy powinienem użyć opcji Dołącz?

Ale generalnie po prostu nie dostaję, gdy użyłbym .Join() lub warunku, który jest przydatny. Czy ktoś może mi to wyjaśnić, tak jakbym był czwartoklasistą? Bardzo proste wyjaśnienie do zrozumienia dostarczy moją odpowiedź na głos.

Odpowiedz

49

Powiedzmy chcesz uruchomić kilka wątków roboczych do wykonywania pewnego rodzaju obliczeń, a następnie zrobić coś potem ze wszystkimi wynikami.

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()); 

O tak, i nie używać Losowo tak, ja po prostu staramy się pisać minimalny, zrozumiałego przykład. Kończy się to nie losowo, jeśli tworzysz zbyt mało instancji nowych losowych, ponieważ ziarno jest oparte na zegarze.

+1

Musisz dodać coś takiego jak "int value = i;" w pętli przed uruchomieniem nowego wątku. Ponieważ "i" może rosnąć przed rozpoczęciem wątku, a suma będzie niedeterministyczna. –

8

Łączenie jest używane głównie wtedy, gdy musisz poczekać, aż wątek (lub ich wiązka) zostanie zakończony przed rozpoczęciem pracy z kodem.

Z tego powodu jest szczególnie przydatna, gdy trzeba zebrać wynik z wykonania wątku.

Zgodnie z poniższym komentarzem Arafangion ważne jest również dołączenie do wątków, jeśli po utworzeniu wątku trzeba wykonać jakiś kod czyszczenia/sprzątania.

+1

Należy wspomnieć, że może to być ważne podczas czyszczenia procesu przygotowującego do wyjścia. – Arafangion

+0

@Arafangion: tak! – Lorenzo

15

W poniższym fragmencie kodu, główny wątek wywołuje Join() który powoduje, że czekać na wszystkich potomnych nici do końca:

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."); 
} 

Zauważ, że jeśli również obrócił się wątek z puli wątków (używając QueueUserWorkItem na przykład), Join nie będzie czekać na ten wątek tła. Będziesz musiał zaimplementować jakiś inny mechanizm, taki jak użycie AutoResetEvent.

uzyskać doskonałe wprowadzenie do gwintowania, polecam czytanie Joe Albahari nic nie kosztuje Threading in C#

+1

dzięki za link do bezpłatnego i wartościowego ebooka –

7

To jest bardzo prosty program do demonstracji użycia wątku Join. Proszę śledzić moje komentarze, aby lepiej zrozumieć.Wybierz ten program, jak jest.

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.First run time jak to jest (z komentarzami): Wtedy wynik będzie 0 (wartość początkowa) lub 1 (po zakończeniu gwint 1) lub 10 (lub nitki zakończone)

2.Run z usunięcie komentarza thread1.Join(): Wynik powinien być zawsze więcej niż 1 .because thread1.Join() zwolniony i gwint 1 powinno być zakończone przed dostać sumę.

3.Run z wyprowadzenie wszystkich komentarzu: wynik powinien być zawsze 11

0

Dodawanie opóźnienie 300ms w metodzie „próbka” oraz opóźnienie 400ms w „sample2” od słupka devopsEMK byłby uczynić go łatwiejsze do zrozumienia.

W ten sposób można to zauważyć, usuwając komentarz z "thread1.Join();" linii, wątek główny czeka na zakończenie "wątku1" i dopiero po wykonaniu ruchów.

0

Innym przykładem, gdy nitka pracownik powiedzmy odczytuje ze strumienia wejściowego, natomiast metoda odczytu może działać zawsze i chcesz jakoś tego uniknąć - poprzez zastosowanie limit czasu przy użyciu innego wątku watchdog:

// 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(); 
    } 
}); 
Powiązane problemy