簡體   English   中英

如何等待線程完成 .NET?

[英]How can I wait for a thread to finish with .NET?

我以前從未在 C# 中真正使用過線程,我需要有兩個線程以及主 UI 線程。 基本上,我有以下幾點。

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

所以,本質上,我怎樣才能讓一個線程等待另一個線程完成? 做這個的最好方式是什么?

我可以看到五個可用選項:

1. 線程.加入

就像米奇的回答一樣。 但這會阻塞你的 UI 線程,但是你會得到一個內置的 Timeout。


2. 使用WaitHandle

ManualResetEvent是 jrista 建議的WaitHandle

需要注意的一點是,如果您想等待多個線程: WaitHandle.WaitAll()默認不會工作,因為它需要一個MTA線程。 您可以通過使用MTAThread標記您的Main()方法來解決此問題 - 但是這會阻止您的消息泵,並且不推薦從我讀過的內容中。


3. 觸發事件

請參閱Jon Skeet 關於事件和多線程的此頁面 ifEventName(this,EventArgs.Empty)之間,一個事件可能會被取消訂閱——它以前發生在我身上。

(希望這些編譯,我沒有嘗試過)

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. 使用委托

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

如果您確實使用了 _count 方法,那么使用它來增加它可能是一個想法(為了安全起見)

Interlocked.Increment(ref _count)

我很想知道使用委托和事件進行線程通知之間的區別,我知道的唯一區別是事件是同步調用的。


5.改為異步執行

這個問題的答案非常清楚地描述了您使用這種方法的選擇。


錯誤線程上的委托/事件

事件/委托的處理方式意味着您的事件處理程序方法位於 thread1/thread2而不是主 UI 線程,因此您需要在 HandleThreadDone 方法的頂部切換回:

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

添加

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

在你啟動它之后,但這不會完成太多,因為它與在主線程上運行的結果基本相同!

如果您想了解 .NET 中的線程,我強烈建議您閱讀 Joe Albahari 的C# 中的線程免費電子書。

如果從 .NET 4 使用此示例可以幫助您:

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:創建多個線程並等待所有線程完成

前兩個答案很棒,適用於簡單的場景。 但是,還有其他方法可以同步線程。 以下也將起作用:

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是 .NET 框架必須提供的各種WaitHandle之一。 它們可以提供比 lock()/Monitor、Thread.Join 等簡單但非常常見的工具更豐富的線程同步功能。

它們還可以用於同步兩個以上的線程,從而允許復雜的場景,例如協調多個“子”線程的“主”線程、相互依賴的多個階段的多個並發進程進行同步等。

你想要Thread.Join()方法,或者它的重載之一。

我會讓你的主線程將一個回調方法傳遞給你的第一個線程,當它完成后,它將調用主線程上的回調方法,它可以啟動第二個線程。 這可以防止您的主線程在等待 Join 或 Waithandle 時掛起。 無論如何,將方法作為委托傳遞對於學習 C# 來說是一件有用的事情。

嘗試這個:

List<Thread> myThreads = new List<Thread>();

foreach (Thread curThread in myThreads)
{
    curThread.Start();
}

foreach (Thread curThread in myThreads)
{
    curThread.Join();
}

我采取了一些不同的方法。 以前的答案中有一個計數器選項,我只是應用它有點不同。 當線程啟動和停止時,我正在關閉許多線程並增加一個計數器並減少一個計數器。 然后在 main 方法中,我想暫停並等待線程完成。

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

這記錄在我的博客文章中:http: //www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/

當我希望 UI 能夠在等待任務完成時更新其顯示時,我使用了一個 while 循環來測試線程上的 IsAlive:

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

此實現與@jrista 基於ManualResetEvent的示例略有不同,因為它顯示了各種選項如何像紅色或綠色交通燈。

    public System.Threading.AutoResetEvent thread1done = new System.Threading.AutoResetEvent(false);

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    {
        thread1done.Set(); //set traffic light to green before threading
        StartTheActions();
    }

    public void StartTheActions()
    {
        Thread t1 = new Thread(action1);

        t1.Start();
        thread1done.WaitOne(); //traffic light is red, until thread1done.Set inside action1()

        Thread t2 = new Thread(action2);
        t2.Start();
    }
    public void action1()
    {
        Thread.Sleep(5000);
        //.... do some work
        thread1done.Set(); //work is done, set traffic light to green at thread1done.WaitOne()
    }
    public void action2()
    {
        MessageBox.Show("Now processing action2");
    }

另一種方法是在一個線程中使用 lock(someObject) 和 Monitor.Wait(someObject[,timeout]),在另一個線程中使用 lock(someObject) 和 Monitor.Pulse(someObject)。 SomeObject 在所有 4 次調用中必須是同一個類的實例。 SomeObject 不能是結構。

第一個線程鎖定 someObject,然后調用 Monitor.Wait() 釋放鎖,因此第二個線程可以鎖定 someObject。 當第二個線程完成時,它調用 Monitor.Pulse(),然后第一個線程的 Monitor.Wait() 結束。

示例: someObject 是一個隊列,第一個線程等待第二個線程將對象放入隊列,然后將該對象出隊。

我以前從未真正在C#中使用過線程,在C#中,我需要有兩個線程以及主UI線程。 基本上,我有以下幾點。

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

因此,從本質上講,我如何讓一個線程等待另一個線程完成? 做這個的最好方式是什么?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM