iT邦幫忙

2021 iThome 鐵人賽

DAY 7
0
自我挑戰組

從C到JS的同步非同步探索系列 第 7

[Day 7] .Net WhenAll 底層(2)

前言

我們今天要試著解決昨天閱讀 WhenAll 留下來的兩個問題

  1. 為何要 "atomic" 的把 task 的總數減一, 看起來只有一條 thread 會動到啊 ?
  2. 如果 WhenAll 在查看 task 狀態時, task 未完成會進入另一條路線, 那條路線發生了什麼呢 ?

另一條路線

當 WhenAll 檢測到尚未完成的任務時會進入 task.AddCompletionAction(this); 這裡的 task 是待完成列表的其中一個任務, this 是運行 WhenAll 的本人。

跳過層層包裝我們會看到

private void AddCompletionAction(ITaskCompletionAction action, bool addBeforeOthers)
{
    if (!AddTaskContinuation(action, addBeforeOthers))
        action.Invoke(this);  
}

AddTaskContinuation(action, addBeforeOthers) 為 false 表示任務已經完成, 在這裡調用 WhenAll 中的 Invoke 把待完成任務總數減1。

接著我們了解一下 AddCompletionAction 裡面發生甚麼事

private bool AddTaskContinuation(object tc, bool addBeforeOthers)
{
    Contract.Requires(tc != null);

    // 檢測目前所在執行列表裡的任務, 是否完成
    if (IsCompleted) return false;

    // 利用 CAS 把 tc ( WhenAllPromis 這個物件 ) 送入連續任務
    if ((m_continuationObject != null) || (Interlocked.CompareExchange(ref m_continuationObject, tc, null) != null))
    {
       // 當一般 CAS 方法失敗後用這個一定成功的 CAS 方法。 不細講
        return AddTaskContinuationComplex(tc, addBeforeOthers);
    }
    else return true; // 回傳 true, 成功把 WhenAllPromise 加入到這個執行列表裡的任務的連續任務區。
}

注 : WhenAllPromise 事當我們調用 WhenAll 所生成的物件, 上一篇文提到的事情都是在, WhenAllPromise 發生的

當 WhenAllPromise 這個物件被送入連續任務後發生了什麼呢 ?

這個疑問會在之後解答, 因為這個 method 就寫到這裡了。

總結

Task.WhenAll( taskList ) 調用後執行動作

taskList = 待完成任務列表, 我自己命名的變數

  1. 創建 WhenAllPromise 物件, 且傳入 taskList
  2. 把 taskList 的長度(視為未完成任務數量)存在 WhenAllPromise 物件中
  3. WhenAllPromise 內部執行依序掃描 taskList 內的 task
  4. 當 task 已完成, 就把 未完成任務數量 減 1 ( 要 atomic , 因為 WhenAllPromise 有可能被掛載到別的 task 執行, 就會被視為 multi-thread )
  5. 當 task 未完成, 就把 WhenAllPromise 的 reference 掛載到 未完成的 task 的連續任務區(這個名詞是我自己取的 XD )
  6. 當一次全部掃描結束, 若是 WhenAllPromise 內的未完成任務數量為0, 表示任務全部完成, 當即設置回傳變數, 與進行回傳。
  7. 有可能未完成任務數量沒有歸零, 表示 WhenAllPromise 有在上面的第 5 步掛載到別的 task。WhenAllPromise 不會結束。至於被掛載進連續任務區的任務會被如何處理, 會在 Day 10 跟大家解釋清楚。

值得注意的是因為每個 task 的連續任務區都有可能在任何時刻, 被別的 thread 添加, 因此也被設計成一個 lock-free data structure.

另外, 未完成任務數量因為WhenAllPromise被掛載到別條 thread , 為了避免 race condition 也用了 atomic 變數。

明日進度

開始看看 task 的創建, 試著了解 task 的生命歷程吧。

明天見!


上一篇
[Day 6] .Net WhenAll 底層(1)
下一篇
[Day 8] .Net Task 底層(1)
系列文
從C到JS的同步非同步探索30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言