2018年6月24日 星期日

【C#】Synchronize thread

多執行緒同步


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

傳統的方法
用 EventWaitHandle(也就是 AutoResetEvent 或 ManualResetEvent) 控制子緒
static AutoResetEvent handleA = new AutoResetEvent(false);
static AutoResetEvent handleB = new AutoResetEvent(false);

static void Main(string[] args)
{
    for (int i = 0; i > 2; i++)
    {
        Task.Run(() =>
        {
            Console.WriteLine("Start to create component A");
            Thread.Sleep(2500);
            Console.WriteLine("Finished component A");

            handleA.Set();
        });

        Task.Run(() =>
        {
            Console.WriteLine("Start to create component B");
            Thread.Sleep(5000);
            Console.WriteLine("Finished component B");

            handleB.Set();
        });

        Console.WriteLine("Wait for components");
        handleA.WaitOne();
        handleB.WaitOne();
        // 上面兩行可以取代成 WaitHandle.WaitAll(new WaitHandle[] { handleA, handleB });
        Console.WriteLine("Assemble the toy");
        Console.WriteLine("Toy created");
    }

    Console.ReadLine();
}
每個需要停等的任務要有一個 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()
for (int i = 0; i < 2; i++)
{
    Task taskA = Task.Run(() =>
    {
        Console.WriteLine("Start to create component A");
        Thread.Sleep(2500);
        Console.WriteLine("Finished component A");
    });

    Task taskB = Task.Run(() =>
    {
        Console.WriteLine("Start to create component B");
        Thread.Sleep(5000);
        Console.WriteLine("Finished component B");
    });

    Console.WriteLine("Wait for components");
    Task.WaitAll(new Task[] { taskA, taskB });

    Console.WriteLine("Assemble the toy");
    Console.WriteLine("Toy created");
}
是不是好懂一百倍? 語意簡單到不用解釋。

沒有留言:

張貼留言