wake-up-neo.net

Wie warte ich, bis der Thread mit .NET fertig ist?

Ich habe Threading noch nie in C # verwendet, wo ich zwei Threads sowie den Haupt-UI-Thread haben muss. Grundsätzlich habe ich folgendes.

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

Im Grunde ist meine Frage, wie man einen Thread warten lässt, bis ein anderer beendet ist. Wie geht das am besten?

161
Maxim Zaslavsky

Ich kann 5 verfügbare Optionen sehen:

1. Thread.Join

Wie bei Mitch's Antwort. Dadurch wird Ihr UI-Thread blockiert, es ist jedoch ein Timeout für Sie eingebaut.


2. Verwenden Sie ein WaitHandle

ManualResetEvent ist ein WaitHandle, wie von jrista vorgeschlagen.

Wenn Sie auf mehrere Threads warten möchten, ist Folgendes zu beachten: WaitHandle.WaitAll() funktioniert standardmäßig nicht, da ein MTA-Thread erforderlich ist. Sie können dies umgehen, indem Sie Ihre Main() -Methode mit MTAThread markieren - dies blockiert jedoch Ihre Nachrichtenpumpe und wird von dem, was ich gelesen habe, nicht empfohlen.


3. Löse ein Ereignis aus

Siehe diese Seite von Jon Skeet zu Ereignissen und Multithreading. Es ist möglich, dass ein Ereignis zwischen dem if und dem EventName(this,EventArgs.Empty) abbestellt wird. Es ist mir passiert Vor.

(Hoffentlich diese kompilieren, ich habe es nicht versucht)

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. Verwenden Sie einen Delegaten

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<T> or Func<T>
        public void Run(object state)
        {
            // Do a task

            Action completeAction = (Action)state;
            completeAction.Invoke();
        }
    }
}

Wenn Sie die _count-Methode verwenden, ist es möglicherweise (aus Sicherheitsgründen) sinnvoll, sie mit zu erhöhen

Interlocked.Increment(ref _count)

Es würde mich interessieren, den Unterschied zwischen der Verwendung von Delegaten und Ereignissen für die Thread-Benachrichtigung zu kennen. Ich weiß nur, dass Ereignisse synchron aufgerufen werden.


5. Tun Sie es stattdessen asynchron

Die Antwort auf diese Frage enthält eine sehr klare Beschreibung Ihrer Optionen mit dieser Methode.


Delegieren/Ereignisse im falschen Thread

Die Event/Delegate-Methode bedeutet, dass Ihre Event-Handler -Methode sich auf Thread1/Thread2 befindet und nicht auf der Hauptbenutzeroberfläche Thread , daher müssen Sie ganz oben in den HandleThreadDone-Methoden zurückwechseln:

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

Hinzufügen

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

nachdem Sie es gestartet haben, wird dies jedoch nicht viel bewirken, da es im Wesentlichen das gleiche Ergebnis wie das Ausführen auf dem Haupt-Thread ist!

Ich kann Joe Albaharis Threading in C # kostenloses E-Book nur wärmstens empfehlen, wenn Sie sich mit Threading in .NET vertraut machen möchten.

55
Mitch Wheat

Die beiden vorherigen Antworten sind großartig und eignen sich für einfache Szenarien. Es gibt jedoch auch andere Möglichkeiten, um Threads zu synchronisieren. Folgendes wird auch funktionieren:

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 ist eine der verschiedenen WaitHandle - Funktionen, die das .NET-Framework bietet. Sie bieten viel umfangreichere Funktionen zur Synchronisierung von Threads als die einfachen, aber weit verbreiteten Tools wie lock ()/Monitor, Thread.Join usw. Sie können auch zum Synchronisieren von mehr als zwei Threads verwendet werden, wodurch komplexe Szenarien wie ein "Master" -Thread möglich werden Das koordiniert mehrere untergeordnete Threads, mehrere gleichzeitige Prozesse, die von mehreren Phasen abhängen, die synchronisiert werden sollen usw.

30
jrista

Wenn Sie .NET 4 verwenden, kann Ihnen dieses Beispiel helfen:

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
    }
}

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

22

Sie möchten die Thread.Join() -Methode oder eine ihrer Überladungen .

5
Daniel Pryden

Ich möchte, dass Ihr Hauptthread eine Rückrufmethode an Ihren ersten Thread übergibt. Wenn dies erledigt ist, ruft er die Rückrufmethode auf dem Hauptthread auf, mit der der zweite Thread gestartet werden kann. Dies verhindert, dass Ihr Haupt-Thread hängt, während er auf Join oder Waithandle wartet. Übergeben von Methoden als Delegaten ist eine nützliche Sache, um mit C # zu lernen.

4
Ed Power

Ich habe einige Zeit damit verbracht, nach einer Lösung zu suchen, wie ich sie mir ausgedacht habe. Also bin ich etwas anders vorgegangen. Es gibt eine Zähleroption oben, ich habe sie nur ein bisschen anders angewendet. Ich habe zahlreiche Threads abgespult und einen Zähler inkrementiert und einen Zähler dekrementiert, während ein Thread gestartet und gestoppt wurde. Dann wollte ich in der Hauptmethode pausieren und warten, bis die Threads abgeschlossen sind.

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

Auf meinem Blog dokumentiert. http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/

0
Adam

Wenn ich möchte, dass die Benutzeroberfläche ihre Anzeige aktualisiert, während auf den Abschluss einer Aufgabe gewartet wird, verwende ich eine while-Schleife, die IsAlive für den Thread testet:

    Thread t = new Thread(() => someMethod(parameters));
    t.Start();
    while (t.IsAlive)
    {
        Thread.Sleep(500);
        Application.DoEvents();
    }

0
Mark Emerson