it-swarm.com.de

Multithreading: Wann würde ich einen Join verwenden?

Ich sehe online, dass es heißt, dass ich myThread.Join(); verwende, wenn ich meinen Thread blockieren möchte, bis ein anderer Thread beendet ist. (Eines der Dinge, die ich nicht verstehe, ist, was passiert, wenn ich mehrere Threads habe).

Aber im Allgemeinen verstehe ich es nicht, wenn ich .Join() oder eine Bedingung verwenden würde, für die es nützlich ist. Kann mir das bitte jemand erklären, als wäre ich ein Viertklässler? Eine sehr einfache Erklärung zu verstehen, wird meine Antwort erhalten.

56
foreyez

Angenommen, Sie möchten einige Worker-Threads starten, um Berechnungen durchzuführen, und anschließend mit allen Ergebnissen etwas tun.

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 ja, und benutze Random nicht so, ich habe nur versucht, ein minimales, leicht verständliches Beispiel zu schreiben. Es ist nicht wirklich zufällig, wenn Sie neue zufällige Instanzen zu nahe an der Zeit erstellen, da der Startwert auf der Uhr basiert.

63
J.D.

Im folgenden Code-Snippet ruft der Haupt-Thread Join () auf, wodurch er wartet, bis alle erzeugten Threads beendet sind:

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

Wenn Sie auch einen Thread aus dem Thread-Pool hochgefahren haben (z. B. mit QueueUserWorkItem), würde Join nicht auf diesen Hintergrund-Thread warten. Sie müssten einen anderen Mechanismus implementieren, beispielsweise die Verwendung eines AutoResetEvent.

Für eine ausgezeichnete Einführung in das Threading empfehle ich, das kostenlose Threading von Joe Albahari in C # zu lesen.

18
Mitch Wheat

Dies ist ein sehr einfaches Programm, um die Verwendung von Thread Join zu demonstrieren. Bitte folgen Sie meinen Kommentaren zum besseren Verständnis. Schreiben Sie dieses Programm so, wie es ist.

    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.Zum ersten Mal wie es ist (mit Kommentaren): Das Ergebnis ist 0 (Anfangswert) oder 1 (wenn Thread 1 beendet ist) oder 10 (oder Thread beendet)

2.Run mit Entfernen des Kommentars thread1.Join(): Das Ergebnis sollte immer mehr als 1 sein, da thread1.Join() ausgelöst und Thread 1 beendet werden sollte, bevor die Summe abgerufen wird.

3.Run mit Entfernen aller Kommentare: Das Ergebnis sollte immer 11 sein

11
Elshan

Join wird hauptsächlich dann verwendet, wenn Sie warten müssen, bis ein Thread (oder mehrere) beendet werden, bevor Sie mit Ihrem Code fortfahren. 

Aus diesem Grund ist es besonders nützlich, wenn Sie das Ergebnis einer Thread-Ausführung sammeln müssen.

Gemäß dem Arafangion-Kommentar unten ist es auch wichtig, Threads zu verbinden, wenn Sie nach dem Erstellen eines Threads einen Reinigungs-/Housekeeping-Code ausführen müssen.

9
Lorenzo

Join stellt sicher, dass die Laufflächen oberhalb der Linie ausgeführt werden, bevor die darunter liegenden Zeilen ausgeführt werden.

3
nayanajith

Das Hinzufügen einer Verzögerung von 300 ms in der Methode "Sample" und einer Verzögerung von 400 ms in "Sample2" aus dem Beitrag von devopsEMK würde das Verständnis erleichtern. 

Auf diese Weise können Sie das beobachten, indem Sie den Kommentar aus "thread1.Join ();" entfernen. Zeile wartet der Haupt-Thread, bis der "Thread1" abgeschlossen ist und erst danach weitergeht.

0
Paul S.

Ein anderes Beispiel: Wenn Ihr Worker-Thread beispielsweise Lesevorgänge aus einem Eingabestrom enthält, während die Lesemethode für immer ausgeführt werden kann und Sie dies irgendwie vermeiden möchten, indem Sie das Zeitlimit mit einem anderen Watchdog-Thread anwenden:

// 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();
    }
});
0
Ladislav