日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

【转】1.A(译).NET4.X 并行任务中Task.Start()的FAQ

發布時間:2023/12/10 asp.net 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】1.A(译).NET4.X 并行任务中Task.Start()的FAQ 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

傳送門:異步編程系列目錄……

?

?????????近期有不少人向我咨詢關于Task的Start()方法。比如:何時使用及何時不使用Start()、Start()又做了些什么……我想在這里回答一些問題試圖澄清和平息任何關于Start()方法是什么以及做了什么的誤解。

?

1.?????????問題:我什么時候能使用Task的Start()方法?

?????????只有Task處于TaskStatus.Created狀態時才能使用實例方法Start()。并且,只有在使用Task的公共構造函數構造的Task實例才能處于TaskStatus.Created狀態。

表示?Task?的生命周期中的當前階段。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public?enum?TaskStatus { ????// 該任務已初始化,但尚未被計劃。 ????Created = 0, ????// 該任務正在等待 .NET Framework 基礎結構在內部將其激活并進行計劃。 ????WaitingForActivation = 1, ????// 該任務已被計劃執行,但尚未開始執行。 ????WaitingToRun = 2, ? ????// 該任務正在運行,但尚未完成。 ????Running = 3, ????// 該任務已完成執行,正在隱式等待附加的子任務完成。 ????WaitingForChildrenToComplete = 4, ? ????// 已成功完成執行的任務。 ????RanToCompletion = 5, ????// 該任務已通過對其自身的 CancellationToken 引發 OperationCanceledException 異常 ????Canceled = 6, ????// 由于未處理異常的原因而完成的任務。 ????Faulted = 7, }

?

2.?????????問題:使用Task.Run()/Task.ContinueWith()/Task.Factory.StartNew()/TaskCompletionSource/異步方法(即使用async與await關鍵字的方法)……應該調用Start()方法嗎?

不應該。不僅不應該,而且也不能,因為此時調用Start()會報異常。從問題1可知:Start()實例方法只適用于TaskStatus.Created狀態的Task。由上面提到的方式創建的Task其狀態不是TaskStatus.Created,而是如TaskStatus.WaitingForActivation、TaskStatus.Running或TaskStatus.RanToCompletion。

?

3.?????????問題:Start()方法實際做了什么?

Start()將任務排隊到目標TaskScheduler(無參的Start()重載任務調度者為TaskScheduler.Current)。當你使用Task的構造函數創建一個Task實例時,它處于創建狀態(TaskStatus.Created),它沒有與任何調度器關聯,也沒有真真被執行。如果你永遠不調用Start()方法,那么此任務永遠不會排隊也不會完成。為了讓任務被執行,它需要在調度器上進行排隊,以便調度器在合適的時刻執行它。在Task上調用Start()方法將改變任務內部的一些數據(eg:狀態從Created改變為WaitingToRun)并且將任務通過TaskScheduler實例的QueueTask()方法排隊到目標調度器。此時,此任務未來的執行掌握在調度器手中,最終會通過TaskScheduler的TryExecuteTask實例方法執行。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ?????// 表示一個處理將任務排隊到線程中的底層工作的對象。 ?????public?abstract?class?TaskScheduler ?????{ ??????????protected?TaskScheduler(); ?? ??????????// 獲取與當前正在執行的任務關聯的 TaskScheduler。 ??????????public?static?TaskScheduler Current { get; } ??????????// 獲取由 .NET Framework 提供的默認 TaskScheduler 實例。 ??????????public?static?TaskScheduler Default { get; } ??????????// 創建一個與當前 SynchronizationContext 關聯的 TaskScheduler。 ??????????public?static?TaskScheduler FromCurrentSynchronizationContext(); ? ??????????// 當出錯的 Task 的未觀察到的異常將要觸發異常升級策略時發生,默認情況下,這將終止進程。 ??????????public?static?event?EventHandler<UnobservedTaskExceptionEventArgs> UnobservedTaskException; ? ??????????// 獲取此 TaskScheduler 實例的唯一 ID。 ??????????public?int?Id { get; } ??????????// 指示此 TaskScheduler 能夠支持的最大并發級別,默認計劃程序返回 System.Int32.MaxValue。 ??????????public?virtual?int?MaximumConcurrencyLevel { get; } ? ??????????// 僅對于調試器支持,生成當前排隊到計劃程序中等待執行的 Task 實例的枚舉。 ??????????protected?abstract?IEnumerable<Task> GetScheduledTasks(); ??????????// 將 Task 排隊到計劃程序中。 ??????????protected?internal?abstract?void?QueueTask(Task task); ??????????// 嘗試將以前排隊到此計劃程序中的 Task 取消排隊。 ??????????protected?internal?virtual?bool?TryDequeue(Task task); ??????????// 嘗試在此計劃程序上執行提供的 Task。執行失敗的常見原因是, ??????????// 該任務先前已經執行或者位于正在由另一個線程執行的進程中。 ??????????protected?bool?TryExecuteTask(Task task); ??????????// 確定提供的 Task是否可以在此調用中同步執行,如果可以,將執行該任務。 ??????????//?? taskWasPreviouslyQueued: ??????????//???? 一個布爾值,該值指示任務之前是否已排隊。如果此參數為 True,則該任務以前可能已排隊(已計劃); ??????????//???? 如果為 False,則已知該任務尚未排隊,此時將執行此調用,以便以內聯方式執行該任務,而不用將其排隊。 ??????????// 返回結果: ??????????//???? 一個布爾值,該值指示是否已以內聯方式執行該任務。 ??????????protected?abstract?bool?TryExecuteTaskInline(Task task, bool?taskWasPreviouslyQueued); }

?

4.?????????問題:我能在同一個Task上調用多次Start()方法嗎?

不行,Task只能離開創建狀態(TaskStatus.Created)一次,而Start()方法使Task離開創建狀態。因此,Start()方法只能使用一次。任何企圖在不是創建狀態的Task上調用Start()都將導致一個異常。Start()方法采用同步方式運行以確保任務對象保持一致的狀態,即使是同時調用多次Start(),也只可能有一個調用會成功。

?

5.?????????問題:使用Task.Start()與使用Task.Factory.StartNew()有何不同?

Task.Factory.StartNew()可快速創建一個Task并且開啟任務。代碼如下:

1 var?t = Task.Factory.StartNew(someDelegate);

這等效于:

1 2 var?t = new?Task(someDelegate); t.Start();

表現方面,前者更高效。就像在問題3中提到的,Start()采用同步方式運行以確保任務對象保持一致的狀態即使是同時調用多次Start(),也可能只有一個調用會成功。相比之下,StartNew()知道沒有其他代碼能同時啟動任務,因為在StartNew()返回之前它不會將創建的Task引用給任何人,所以StartNew()不需要采用同步方式執行。

?

6.?????????問題:我聽說Task.Result屬性也能開啟任務,真的嗎?

不能,只有兩種方式可能使Task離開其創建狀態

1)?????????將一個CancellationToken傳遞給Task的構造函數,并且這個token已經或稍后請求取消。如果Task任然處于Created/WaitingForActivation/WaitingToRun,當token取消時,Task將改變為TaskStatus.Canceled狀態。

2)?????????在Task上調用Start()。

因此,?Result屬性不能開啟任務。如果對處于創建狀態的Task實例上調用Wait()方法或Result屬性,這個調用將被阻塞,需要等待開啟任務,這樣它就可以排隊到調度器,調度程序最終會執行它,然后完成任務。被阻塞的調用就被喚醒。

?????????你可能會認為不是這樣的。Result能開啟任務,但是這只適用于“內聯”任務的執行。如果一個任務已經排隊到TaskScheduler,但是這個任務可能任然保持在調取器的任務隊列中。當你對被排隊的任務請求Result屬性時,運行時將嘗試內聯任務的執行而不是純粹的阻塞和等待調度器在未來某個時刻使用其他線程來完成任務執行。因此,調用Result屬性可能終止于TaskScheduler的TryExecuteTaskInline()方法的調用,并且如何處理請求由TaskScheduler決定。

?

7.?????????問題:我應該提供返回未啟動任務的公共APIs嗎?

更恰當的問題是“我應該提供處于創建狀態任務的公共APIs嗎”,答案是“不能”。

?????????基本原因是:當你正常調用同步方法,該方法將很快被調用執行。對于返回Task的方法,你可以把Task看做是異步方法完成的結果。但是這并不能改變需要調用Start()方法開始相關操作的事實。因此,返回一個處于創建狀態的Task的異步方法是很奇怪的,只是想代表一個沒有開始的操作?

?????????因此,如果你有一個返回Task的公共方法,并且Task是使用構造函數創建的,請確保你在返回Task之前開啟任務。否則,很可能在APIs使用方導致死鎖或類似的問題,因為使用方期待調用完成時Task也最終完成,但如果返回一個尚未啟動的任務,它將永遠不會完成。有些框架允許你參數化方法或委托,返回Task甚至驗證返回任務的狀態,如果Task還是創建狀態就為其拋出異常。

?

8.?????????問題:我應該使用Task的構造函數?+ Task的Start()實例方法嗎?

在大多數情況下,你最好使用一些其他機制。比如,如果你只是想計劃一個任務來運行你提供的委托,你最好使用Task.Run()靜態方法或Task.Factory的StartNew()實例方法,而不是使用Task的構造函數創建一個任務再調用Start()開啟它,這不僅僅減少了代碼量,并且更加高效(見問題5回答),另外還可以減少犯錯的可能,比如忘記開啟任務。

當然,在一些情況下使用Task的構造函數+Start()更加有意義。比如,需要根據某些原因來選擇傳遞而來的Task,然后再使用Start()方法來實際排隊任務。

另外,一個更明顯的示例是,如果你想獲得任務本身的引用,可能使用了如下代碼:

1 2 Task theTask = null; theTask = Task.Run(() => Console.WriteLine(“My ID is?{0}.”, theTask.Id));

?????????有問題,存在競爭?在Task.Run()方法內部,會創建一個新Task對象并且將其排隊到線程池調度器中。如果線程池比較空閑,那么會立即分配一個輔助線程開始執行任務。新創建的任務最后會存儲在theTask變量中,輔助線程和調用Task.Run()的線程會發生競爭的訪問此變量。我們能解決這種競爭通過分離構造函數與TaskScheduler:

1 2 3 Task theTask = null; theTask = new?Task(() =>Console.WriteLine(“My ID is?{0}.”, theTask.Id)); theTask.Start(TaskScheduler.Default);

?????????現在我們已經確保Task實例將在線程池執行任務之前被存儲到theTask變量。因為線程池在Task對象調用Start()排隊任務之前無法獲得Task對象的引用,并且在這個時候,變量theTask已經設置為Task的引用,與后面線程池訪問theTask.Id不會存在競爭問題。

?

推薦并行任務相關資源:《關于Async與Await的FAQ》

?

================================================================================================

?

園友提醒:(.NET4.5對.NET4.0的并行任務進行過改進,然而我正式學習并行任務的時候已經是.NET4.5,所以對于新改進的API沒有進行整理了,這邊有園友提醒,做下記錄,方便大家。)

? ? ??@zhangweiwen(Task.Run()是.net4.5新提供的API)

?

================================================================================================

?

?????????Ok,看完此文,相信你對并行任務中關于任務開啟又有更深入的理解了,(*^_^*),喜歡還請多多推薦。

?

原文:http://blogs.msdn.com/b/pfxteam/archive/2012/01/14/10256832.aspx

作者:Stephen Toub

?

?


作者:滴答的雨
出處:http://www.cnblogs.com/heyuquan/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

總結

以上是生活随笔為你收集整理的【转】1.A(译).NET4.X 并行任务中Task.Start()的FAQ的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。