2018年6月24日 星期日

【C#】Synchronize thread

多執行緒同步


某玩具需要 零件A、B組裝,零件由個別的產線製作,假設製作速度 A>B;那 A完成後就要在組裝台等 B 送來才能拼組。 在這個情境中 產線 A、B 即是 thread,生產零件是 thread 進行的任務;A 完成後等待 B 的這個行為就是多緒同步。

傳統的方法
用 EventWaitHandle(也就是 AutoResetEvent 或 ManualResetEvent) 控制子緒
  1. static AutoResetEvent handleA = new AutoResetEvent(false);
  2. static AutoResetEvent handleB = new AutoResetEvent(false);
  3.  
  4. static void Main(string[] args)
  5. {
  6. for (int i = 0; i > 2; i++)
  7. {
  8. Task.Run(() =>
  9. {
  10. Console.WriteLine("Start to create component A");
  11. Thread.Sleep(2500);
  12. Console.WriteLine("Finished component A");
  13.  
  14. handleA.Set();
  15. });
  16.  
  17. Task.Run(() =>
  18. {
  19. Console.WriteLine("Start to create component B");
  20. Thread.Sleep(5000);
  21. Console.WriteLine("Finished component B");
  22.  
  23. handleB.Set();
  24. });
  25.  
  26. Console.WriteLine("Wait for components");
  27. handleA.WaitOne();
  28. handleB.WaitOne();
  29. // 上面兩行可以取代成 WaitHandle.WaitAll(new WaitHandle[] { handleA, handleB });
  30. Console.WriteLine("Assemble the toy");
  31. Console.WriteLine("Toy created");
  32. }
  33.  
  34. Console.ReadLine();
  35. }
每個需要停等的任務要有一個 handle,它的建構引數 initState = false 表示 unsignaled,在 unsignaled 的狀態下 handle.WaitOne() 會 block 當前的 thread,直到 handle.Set() 送出釋放訊號為止。
在 Demo 中我們在兩個 thread 開始後就用 handleA, B 都 WaitOne()把主緒 block 住,等待個別任務的最後一行 Set() 對 handle 個別解鎖;兩個 handle 都解鎖之後主緒即恢復運行。

AutoResetEvent vs  ManualResetEvent
兩者的差異就就像字面上一樣,ManualResetEvent 在 Set() 之後需要手動 Reset() 調整回 unsignal 的狀態,否則 WaitOne() 將不會發生作用;把前面 Demo 的 AutoResetEvent 都改成 ManualResetEvent 即可觀察到第二 loop 時 WaitOne 未 block 主緒。
ref: MSDN

Quick sum up
1. 一個任務,一個 handle
2. EventWaitHandle 的 ctor 一般是給 false,因為我們希望它一開始就是可以 block 緒的狀態
3. handle.WaitOne() 會 block 當前的 thread;handle.Set() 則會釋放
4. AutoResetEvent 比較常用,因為不用特意去 Reset()

傳統方法不管是寫還是閱讀都不甚直觀,之所以還是提一下是因為常 mantain 到,來看看新方法吧!

TPL Task.WaitAll()
  1. for (int i = 0; i < 2; i++)
  2. {
  3. Task taskA = Task.Run(() =>
  4. {
  5. Console.WriteLine("Start to create component A");
  6. Thread.Sleep(2500);
  7. Console.WriteLine("Finished component A");
  8. });
  9.  
  10. Task taskB = Task.Run(() =>
  11. {
  12. Console.WriteLine("Start to create component B");
  13. Thread.Sleep(5000);
  14. Console.WriteLine("Finished component B");
  15. });
  16.  
  17. Console.WriteLine("Wait for components");
  18. Task.WaitAll(new Task[] { taskA, taskB });
  19.  
  20. Console.WriteLine("Assemble the toy");
  21. Console.WriteLine("Toy created");
  22. }
是不是好懂一百倍? 語意簡單到不用解釋。

沒有留言:

張貼留言