[C#.NET][TPL] 等待所有任務完成
在 TPL 裡 Task.WaitAll / Parallel.Invoke / Parallel.For / Parallel.ForEach 方法,都會等待所有工作完成 (也就是所謂的執行緒堵塞或稱為鎖定),才會再往下一行執行,若工作需要有順序就不要用它們。
PS.以下程式碼裡的迴圈沒有太大的意義,只是用來模擬耗時的工作。
內文章節:
- 因為 Task.WaitAll 會堵塞 UI 執行緒,並等待所有任務完成,所以我另外使用一個 Task 來啟用它。
- 工作彼此之間沒有順序。
private void DoWork10() { Task mainTask = new Task(() => { Task t1 = new Task(() => { Thread.CurrentThread.Name = "t1"; ulong result = 0; for (int i = 0; i < 10000; i++) { result++; var str = result.ToString(); Thread.Sleep(1); } }); Task t2 = new Task(() => { Thread.CurrentThread.Name = "t2"; ulong result = 0; for (int i = 0; i < 10000; i++) { result++; var str = result.ToString(); Thread.Sleep(1); } }); t1.Start(); t2.Start(); Task.WaitAll(t1, t2); MessageBox.Show("DONE"); }); mainTask.Start(); }
Task.WaitAll 堵塞了執行緒,它會等待所有的工作完成才會執行下一行,它本身也是一條執行緒。
當然也可以設定等待的時間,以下程式碼表示等待(超時) 2 秒:
Task.WaitAll(new Task[] { t1, t2 }, 2000); MessageBox.Show("DONE"); Console.WriteLine("t1:" + t1.Status); Console.WriteLine("t2:" + t2.Status);
PS.解除執行緒堵塞,Task.Status 仍然等於 Running你可能還會需要 Task.WaitAny 方法,只要任一工作完成就解除堵塞,目前還想不到有什麼工作流程可以使用它。
繼續延用上一個程式碼。
- 用法相當簡單只要把方法體丟進去就好,在這裡我使用 Lambda。
- 因為 Parallel.Invoke 會堵塞 UI 執行緒,並等待所有任務完成,所以我另外使用一個 Task 來啟用它。
- 這讓程式碼比上個例子看起來精簡了一些。
- 若需要像上個例子一樣解除堵塞,它就不適合使用。
- 任務沒有執行順序。
private void DoWork12() { Task mainTask = new Task(() => { Parallel.Invoke(() => { Thread.CurrentThread.Name = "t1"; ulong result = 0; for (int i = 0; i < 10000; i++) { result++; var str = result.ToString(); Thread.Sleep(1); } }, () => { Thread.CurrentThread.Name = "t2"; ulong result = 0; for (int i = 0; i < 10000; i++) { result++; var str = result.ToString(); Thread.Sleep(1); } }); MessageBox.Show("DONE"); }); mainTask.Start(); }
由下圖得知,使用 Parallel.Invoke,TPL 內部會自動幫我們把程式碼切成多個 Task,並分派給 CPU,並且會等待所有 Task 都完成。
- 平行運算的迴圈沒有執行順序,若要使用一定要確定工作流程沒有順序,若非得使用得謹慎處理。
- 因為 Parallel.For 會堵塞 UI 執行緒,所以我另外使用一個 Task 來啟用它。
private void DoWork13() { Task t = new Task(() => { Parallel.For(1, 1000, _ => { SpinWait.SpinUntil(() => false, 1000); }); MessageBox.Show("DONE"); }); t.Start(); }
由下圖得知,Parallel.For 會將工作拆成多個 Task
Parallel.ForEach就不舉例了
更多的資料請參考:
http://msdn.microsoft.com/zh-tw/library/dd460705.aspx
http://msdn.microsoft.com/zh-tw/library/dd537610.aspx
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET