My friends, my life, my style - James S.F. Hsieh

11/16/2009

The structure of Task Parallel Library in .NET 4.0

Parallel programming 是當紅的東西, 當然 .NET 4.0 也把相對應的功能納進去, 不同於 OpenMP或是 Go language 的方式, .NET 4.0 是以函式庫的方式來包裝對應的功能. 上回我介紹了Thread , SynchronizationContext 與 Dispatcher 的關係, 我們可以了解 SynchronizationContext 封裝了一個重要的特性: 就是用來描述某個工作如果在當下的 context 下需要以非同步的方式來運作, 應該用什麼方式來工作比較恰當. 預設的方式當然就是使用 thread pool 的方式來工作嘍, 但是 WPF 是有 thread affiliate 的, 因為 WPF 大部分的物件都繼承至 DispatcherObject, 而 DispatcherObject 是會 binding 於特定的 thread, 原因是確保 thread-safety, 其實跟 COM 的 Apartment 沒什麼不同. 而 TPL 也根據這樣的設計做了相對應的調整!!



基本上, .NET 4.0 中的 TaskScheduler 只有兩種, 一種是 ThreadPoolTaskScheduler 另一種是 SynchronizationContextTaskScheduler, 如 class 的名稱所示, ThreadPoolTaskScheduler 會將接收到的工作直接轉給 global thread pool 去執行, 而 SynchronizationContextTaskScheduler 則會交給 TaskScheduler 建立時所對應的SynchronizationContex, 而 SC 目前則可以有多種:

  1. SynchronizationContext: 交由 global thread pool 去執行
  2. DispatcherSynchronizationContext: 交由 dispatcher 去執行
  3. WindowsFormsSynchronizationContext: 交由 binding control 去執行
  4. ComPlusSynchronizationContext: 交由 COM+ 的 IServiceActivity 去執行
  5. AspNetSynchronizationContext: 沒研究 Orz...
這兩種 class 都是 internal sealed 所以我們只能從幾個方式建立/取得 TaskScheduler :
  1. TaskScheduler.Default : Gets the default TaskScheduler instance.
  2. TaskScheduler.Current : Gets the TaskScheduler associated with the currently executing task.
  3. TaskScheduler.FromCurrentSynchronizationContext : Creates a TaskScheduler associated with the current SynchronizationContext.
整個 Process 會有一個 default 的 ThreadPoolTaskScheduler, 這個 scheduler 會在 TaskScheduler 的 static constructor 中被建立出來, 而 TaskScheduler.Default 就是拿到這個 default 的 scheduler. 如果一個 Task 是巢狀的, 想得到當下執行 task 所對應的 scheduler 則可以使用 TaskScheduler.Current 如果當下沒有 Task 就會拿到 default 的 scheduler, 而 TaskScheduler.FromCurrentSynchronizationContext 怎會根據的當下的 SynchronizationContext 來建立一個 "新" 的 scheduler. 

很明顯的一個 Task 要給哪種 scheduler 來執行是跟 context 有關的, 如同上述所說, 如果一個 Task 是跟 WPF UI 更新有關 (或是 ASP, COM+ 相關的工作等等...), 那就必須丟到對應的 SynchronizationContextTaskScheduler 去執行, 否則只需要用 TaskScheduler.Current, 就足夠了. 下次我們來討論 Task 跟 TaskScheduler 的一些細節. :)