只有累積,沒有奇蹟

2023年8月8日 星期二

[NET] Task.Run 與 Task.Factory.StartNew

前言 
從 .NET 4.0 版本新增 TPL (Task Parallel Library) 函式庫,常在公司專案 Code 中可以看到 Task.Run 與 Task.Factory.StartNew 兩種建立任務的方式,某次同事也有提問對兩者的差異性不太明白,因此這篇文章整理自己對於兩者的相關資訊與用法,希望有不清楚或是自己研究錯誤的地方歡迎提出討論

探索問題
Task.Run 與 Task.Factory.StartNew 使用方式 Sample Code 如下
static void Main(string[] args)
{
    // Task.Factory.StartNew
    Task.Factory.StartNew( () => doSomething());
    
    // Task Run
    Task.Run( () => doSomething() );
}

private static void doSomething()
{
    Console.WriteLine($"Thread Number #{Thread.CurrentThread.ManagedThreadId}");
}
在 .NET 4.0 的時代,可以透過 Task.Factory.StartNew 靜態方法啟用或建立一個新的任務,此靜態方法提供開發者自行定義建立新的 task 任務處理機制,例如控制 TaskScheduler 行為、cancellationToken、creationOptions 等參數,提供相當多的多載方法可依據不同情境使用適合的參數,MSDN 文件也詳細記錄這些參數加上 sample code說明,但反過來看除非知道這些參數的定義與影響程度,使用上很有可能造成使用不正確的狀況發生
在 .NET 4.5 提供了 Task.Run 方法,不需要使用這麼多參數讓開發者在使用上更為方便,實際上 Task.Run 是根據 Task.Factory.StartNew 相同邏輯實現,將 Action 帶入其他參數帶入預設值,舉例來說
Task.Run(doSomething);
可以視為
Task.Factory.StartNew(doSomething,CancellationToken.None,TaskCreationOptions.DenyChildAttach,TaskScheduler.Default);
如果有簡單的任務需求,可以透過 Task.Run 來達成你的需求,使用 Thread Pool 預設的機制來處理新增的 Task 任務 ( TaskScheduler.Default ),也可以說 Task.Run 是 Task.Factory.StartNew 安全參數的縮寫。

Task.Factory.StartNew 使用情境
當 Task.Run 的方法不夠用,或是需求上有更進階的應用情境時可以使用 Task.Factory.StartNew 來完成,看起來很像 PM 說的話,那麼甚麼情境適合用 Task.Factory.StartNew 呢? MSDN 建議有下列情境建議使用  
  • 自訂 TaskCreationOptions : Task.Run 預設的 TaskCreationOptions 參數為 DenyChildAttach,如果需要自訂 TaskCreationOptions 選項時,則需要使用 Task.Factory.StartNew 多載方法
  • 自訂 TaskScheduler : Task.Run 是使用預設的工作排程,如果有需要自訂此參數則不支援,此時也可以透過 Task.Factory.StartNew 達到
舉個例子,某個任務是需要長時間運行的不希望 Task 在完成後 ThreadPool 就立即回收,使用 Task.Run 靜態方法無法實現此需求,也無法定義 TaskCreationOptions 為 LongRunning,此時就需要使用到 Task.Factory.StartNew 指定 creationOptions,範例如下
Task.Factory.StartNew( () => doSomething(), creationOptions:TaskCreationOptions.LongRunning);
如果透過以上簡單案例還是不明白,或許可以再參考另一篇由 微軟 MVP Stephen Cleary 所撰寫的文章 StartNew is Dangerous,文章內容與問題有更多情境與說明,可以在做開發時提供更多的思考方向來決定使用哪種更適合解決當下的問題。

感想
透過以上的說明,希望可以幫助各位了解 Task.Run 與 Task.Factory.StartNew 兩者使用情境與差異,但這只是屬於 Task Parallel Library 的一小部分,近幾年在工作使用上越來越多情境需要用到,日後有時間也會整理更多相關筆記,讓自己對這方面了解更多

參考
Task.Run vs Task.Factory.StartNew
On Task.Factory.StartNew and Task.Run methods
TPL(Task Parallel Language) 
以工作為基礎的非同步程式設計
Regarding usage of Task.Start() , Task.Run() and Task.Factory.StartNew()

0 意見:

張貼留言

Copyright © m@rcus 學習筆記 | Powered by Blogger

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com