1. 程式人生 > >.Net異步編程進階之(二)

.Net異步編程進階之(二)

con mage ack 問題 eth 情況 情況下 core https

  避免使用Task.Result和Task.Wait

  只有極少方法可以正確的使用Task.Result和Task.Wait,一般情況下建議是完全避免在代碼上出現。

 

 技術分享圖片同步異步

  這裏說的同步異步是指把異步操作通過Task.Result或Task.Wait阻塞線程轉為同步,使用Task.Result和Task.Wait阻塞等待異步操作比起真正調用同步操作的API要糟糕的多,下面是經常發生的情況。

  • 啟動異步操作
  • 阻塞調用線程等待操作結果
  • 當異步操作完成時,繼續執行等待操作的代碼。

  結果就是使用了2個線程來完成了只需要1個線程即可完成的同步操作,這往往會導致線程池饑餓最終導致服務中斷。

  技術分享圖片死鎖

  在ASP.NET (Web Forms)、Windows Forms、WPF、UWP的UI線程上如果使用Task.Result和Task.Wait都會導致死鎖。因為這些程序的UI都是單線程的,這些程序會在構造函數裏會把UI線程的Task的同步上下文設置為SynchronizationContext的實例。如果UI線程出現阻塞,Task會在完成任務時候把等待UI線程Task的SynchronizationContext實例用來同步上下文,但UI線程又在等待Task完成,結果就導致死鎖。由於這個原因,“聰明”的開發者想出了各種辦法試圖阻塞線程得到結果。事實上,阻塞異步任務並沒有什麽好方法。

  

技術分享圖片註意:ASP.NET Core 並沒有SynchronizationContext,所以並不容易出現死鎖問題。

技術分享圖片有時候開發人員會編寫的一些"巧妙"的代碼去試圖避免死鎖去同步異步

 1 public string DoOperationBlocking()
 2 {
 3     return Task.Run(() => DoAsyncOperation()).Result;
 4 }
 5 
 6 public string DoOperationBlocking2()
 7 {
 8     return Task.Run(() => DoAsyncOperation()).GetAwaiter().GetResult();
9 } 10 11 public string DoOperationBlocking3() 12 { 13 return Task.Run(() => DoAsyncOperation().Result).Result; 14 } 15 16 public string DoOperationBlocking4() 17 { 18 return Task.Run(() => DoAsyncOperation().GetAwaiter().GetResult()).GetAwaiter().GetResult(); 19 } 20 21 public string DoOperationBlocking5() 22 { 23 return DoAsyncOperation().Result; 24 } 25 26 public string DoOperationBlocking6() 27 { 28 return DoAsyncOperation().GetAwaiter().GetResult(); 29 } 30 31 public string DoOperationBlocking7() 32 { 33 var task = DoAsyncOperation(); 34 task.Wait(); 35 return task.GetAwaiter().GetResult(); 36 }

  使用await取代Task.ContinueWith

  在引入async / await 之前 Task已經存在了不依賴於語言關鍵字的函數 Task.ContinueWith 就是其中一個,雖然這些方法仍然可以使用,但我通常還是建議你使用 await 而不是Task.ContinueWith,事實上,Task.ContinueWithawait 並不是語法上不一樣而已,Task.ContinueWith 不會保存任何類型的狀態( ExecutionContext / SynchronizationContext ),如果沒有提供回調,則默認會回到原來的線程上繼續下接下來的操作。await 關鍵字則會在Task完成的時候保存狀態到當前的線程然後再繼續接下來的操作。

技術分享圖片這個案例使用的是 Task.ContinueWith 而不是 await

1 public Task<int> DoSomethingAsync()
2 {
3     return CallDependencyAsync().ContinueWith(task =>
4     {
5         return task.Result + 1;
6     });
7 }

技術分享圖片這個案例使用關鍵字 await 獲取異步結果

1 public async Task<int> DoSomethingAsync()
2 {
3     var result = await CallDependencyAsync();
4     return result + 1;
5 }

  

.Net異步編程進階之(二)