iT邦幫忙

2021 iThome 鐵人賽

DAY 14
1
自我挑戰組

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

[Day 14] .Net 非同步概念整理

  • 分享至 

  • twitterImage
  •  

前言

本來只是想簡單帶過 .Net , 但不知不覺就寫了一大堆, 覺得內容有些混亂, 就在今天花些時間把這些篇的內容作個總整理吧。

Task 的底層

  1. Task 可以被視為 ThreadPool 的介面, 非同步的 Task 就直接把任務丟給 ThreadPool 後繼續往下做, 同步的 Task 則是任務丟給 ThreadPool 後閒置自己, 等待 ThreadPool 把任務完成。
  2. Task 被投入要執行的任務後會有以下行為
    1. 將當前 thread 的內容記錄, 之後才能進行 Thread 切換
    2. 依情況設置排程器, 本篇有提到兩種排程器, 分別是 SynchronizationContextTaskScheduler 和 threadpool
    3. 若是排程器選擇 threadpool 且檢測為超長任務, 用 Thread 創建一個新 thread 執行
    4. 若是排程器選擇 threadpool 且檢測不為超長任務, 把任務推入 Threadpool 執行
    5. 若是排程器選擇 SynchronizationContextTaskScheduler , 把任務推入 Threadpool 執行, 但是使用一個外來方法要求 Threadpool 用同步方式執行此任務。 這就是一種 .Net 中 await 的實踐方法
    6. 當 c~e 完成後, 執行 Task 中的連續任務區
    7. 若是正在執行的 thread 還能承載連續任務, 就直接接著執行連續任務
    8. 若是正在執行的 thread 還不能承載連續任務, 就直接把連續任務推入 Threadpool 執行
    9. 當連續任務區為空, Task 完成
  3. Threadpool 是一個早已被創建在系統中的資料結構, 其可以推入任務要求執行。參考 : https://zh.wikipedia.org/wiki/线程池
  4. 但 ThreadPool 只是一種概念, .Net 中的實踐的方法是 workQueue , 且排程部分調用外部資源處理。參考 linux 的 workQueue : https://www.kernel.org/doc/html/v5.0/core-api/workqueue.html
  5. 簡易描述 .Net 中的 ThreadPool 就是 :
    • .Net 會事先幫程式創建多個閒置的 thread, 每個 thread 內部都有存放區
    • .Net 還會創建一個特殊的排程 thread , 他會依照每個 thread 內部存放區的狀態決定該喚醒哪個 thread ,該閒置哪個 thread。 所以沒有任務的 thread 會閒置, 有任務的 thread 會被喚醒。
    • 每個醒著的 thread 就會執行自己內部存放區的任務。

本篇沒提到的一些小知識

  1. C# 中的 async/await 是一種語法糖, 在編譯後會被 Task 取代, 所以本篇文章才沒有細講他們。
  2. Thread 的功能是創建一條新 thread 來運行放入的任務, 和 Task 放入已經創建的 TP 是不同的。

利用 WhenAll 為 Sample 演示一群 Task 的調用

其中第 1 ,2 ,3 ,12 步新的, 讀過前面的流程的讀者, 只要看這三步就好。

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

  1. 利用 Task.Run 打包多個任務
  2. 為每個任務創建 task 資料結構, 並且丟入 TP 交給他們執行
  3. 執行 WhenAll 方法, 且傳入第2步創建的多個 task , 為了方面之後用 taskList 描述。
  4. 創建 WhenAllPromise 物件, 且傳入 taskList
  5. 把 taskList 的長度(視為未完成任務數量)存在 WhenAllPromise 物件中
  6. WhenAllPromise 內部執行依序掃描 taskList 內的 task
  7. 當 task 已完成, 就把 未完成任務數量 減 1 ( 要 atomic , 因為 WhenAllPromise 有可能被掛載到別的 task 執行, 就會被視為 multi-thread )
  8. 當 task 未完成, 就把 WhenAllPromise 的 reference 掛載到 未完成的 task 的連續任務區(這個名詞是我自己取的 XD )
  9. 當一次全部掃描結束, 若是 WhenAllPromise 內的未完成任務數量為0, 表示任務全部完成, 當即設置回傳變數, 與進行回傳。
  10. 有可能未完成任務數量沒有歸零, 表示 WhenAllPromise 有在上面的第 5 步掛載到別的 task。WhenAllPromise 不會結束。
  11. 此時執行 Task.WhenAll( taskList ) 留在原地閒置。
  12. 當被 WhenAllPromise 掛載到的任務被 Threadpool 完成, 其會接著運行被掛載的 WhenAllPromise 中的Invoke 方法。
  13. Invoke 方法會 atomic 的把未完成任務數量減 1 , 且若減完 1 未完成任務數量為 0 時, WhenAllPromise 當即設置回傳變數, 與進行回傳。
  14. 整個 Task.WhenAll( taskList ) 順利回傳 taskList 執行結果, 主程式繼續往下運行。

明天進度

試著寫文章後, 發現一些平常一下就過去的東西撰寫出來卻要很久, 更不要說要寫得好了。

帶來的結果就是必須刪減內容以及進度趕不上預期 QQ

明天開始就進入後半部分了, 我們會試著讀 node.js, 之所以要讀 node 有幾個原因, 最主要就是我是 node 後端, 對這個比較有興趣。 其二是, 他的 schedule 部分也在 github 上很好找, 不會像 .Net 一樣找不到, 不會有種意尤未盡的感覺。

為了幫助大家理解之後要讀的 node.js , 會先開始聊一下 IOCP 和 epoll 等概念。


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

尚未有邦友留言

立即登入留言