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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C# :异步编程的注意点

發布時間:2023/12/4 C# 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C# :异步编程的注意点 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在上一篇《C#:異步編程中的 async 和 await》 中簡單介紹了在 C# 中的異步編程以及 async 和 await 編程模型,本文介紹下異步編程的注意事項,主要有以下幾個方面。

同步中調用異步

在同步代碼中調用異步代碼,容易導致死鎖,所以在實際使用異步編程時,推薦的做法是一直異步到底。先來看一個會出現死鎖的代碼:

class?Program {static?void?Main(string[]?args){while?(true){Task.Run(MethodSync);Thread.Sleep(100);}}static?void?MethodSync(){//string?result?=MethodAsync().Result;MethodAsync().Wait();}static?async?Task<string>?MethodAsync(){await?Task.Run(()?=>{Thread.Sleep(2000);});Console.WriteLine("MethodAsync?End");return?"success";} }
  • Main ?方法中使用 Task.Run 進行新的任務的創建,每個間隔 100 ?毫秒,模擬多次請求;

  • 在同步方法 MethodSync 中調用異步方法 MethodAsync;

  • 同步方法中使用 .Result ?或者調用 Wait() 方法進行等待;

運行上面代碼,控制臺會輸出幾次 MethodAsync End 后就會停止,這時死鎖已經發生。可以觀察到控制臺程序使用的線程數會不斷增加:

發生死鎖的原因是:
  • 程序運行時,有一個線程 A 開始執行同步方法 MethodSync ,執行到同步方法中的 .Result 或 Wait() 時,會產生一個線程 B 進行異步方法的調用;

  • 線程 A 會等著 線程 B 完成,然后線程 A 才繼續執行后面的代碼;

  • 當并發比較大的時候,線程池的線程不夠用,需要創建新的線程,創建線程的速度趕不上 Task 創建的速度的時候,就會造成堵塞,最終死鎖。

只需要將 MethodSync 同步方法修改為異步就可以解決此問題:

static?async?Task?MethodASync1() {await?MethodAsync(); }
  • 程序運行時,有一個線程 A 開始執行異步方法 MethodASync1 ,執行到 await 時,會產生一個線程 B 進行異步方法 MethodAsync 的調用;

  • 線程 A 不會等著 線程 B 完成,而是會被線程池收回做其他的事情;

  • 當線程 B 完成后,線程池會重新分配新的線程來進行后續的處理,所以整個過程不會有堵塞。

當然,有些時候我們需要在同步方法中調用異步方法,有下面兩個方法:

  • 借助這個組件來進行處理:https://github.com/StephenCleary/AsyncEx ;

  • 使用 ConfigureAwait(false) 。

合理使用 void 返回值

  • 使用 void 無法確定方法在什么時候調用完成,因為沒有任何內容返回,不像 Task 的返回值,可以獲取到相關的狀態;

  • 返回 void 的異步方法沒有辦法在調用的時候使用 await ;

  • 對 void 方法進行調用時無法捕獲異常。

因為上面的原因,所以我們在寫代碼時盡量不要在異步方法上返回 void ,但有兩種情況也還是可以使用 void 返回值:

1、事件,比如在 Winform 程序中的按鈕事件

private?void?btnTest_Click(object?sender,?EventArgs?e)???????? {????????????await?WriteLog();???????? }

如果要將 btnTest_Click 的返回值修改為 async Task ,編譯時會報錯。

2、記錄日志之類的方法,或者說該方法執行的操作和主任務關系不大,無需知道處理的結果時。

異常處理

當我們編寫同步代碼時,常用 try catch 來進行異常捕獲,例如下面代碼:

class?Program {static?void?Main(string[]?args){try{TestException();}catch?(Exception?ex){//TestException?方法拋出的異常會在這里被捕獲Console.WriteLine(ex.Message);}}static?void?TestException(){throw?new?Exception("Test?Exception");} }

同樣的方式對異步方法進行 try catch ,會發現 catch 中的代碼并沒有執行:

class?Program {static?void?Main(string[]?args){try{TestExceptionAsync();}catch?(Exception?ex){//此處不會被調用Console.WriteLine(ex.Message);}Console.WriteLine("main?end");Console.ReadLine();}static?async?Task?TestExceptionAsync(){await?Task.Delay(200);throw?new?Exception("Test?TestExceptionAsync");} }

要對異常方法進行異常捕獲,必須使用 await 修飾符 、調用 Wait() 方法或者訪問 Result 屬性:

static?async?Task?Main(string[]?args) {try{//var?result?=?TestExceptionAsync().Result;//TestExceptionAsync().Wait();await?TestExceptionAsync();}catch?(Exception?ex){Console.WriteLine(ex.Message);}Console.WriteLine("main?end");Console.ReadLine(); }

在異步方法的返回類型 Task 類中,有一個 Exception 屬性,該屬性返回的類型為 AggregateException ,而在 AggregateException 的內部又有一個 InnerExceptions 屬性用來包裝所有異常的集合。

對于使用 await 修飾符和調用 Wait() 方法、訪問 Result 屬性對于異常的捕獲是有區別的:

Wait() 、Result

當使用Wait 或 Result 的時候,異步方法是將自身的 AggregateException 對象往上拋,這樣在異常處理的時候就會比較麻煩,我們需要這樣來進行異常的解析:

static?async?Task?Main(string[]?args) {try{TestExceptionAsync().Wait();}catch?(AggregateException?aggregateException){foreach?(var?ex?in?aggregateException.InnerExceptions){Console.WriteLine(ex.Message);}}Console.WriteLine("main?end");Console.ReadLine(); }

如果直接獲取 aggregateException 的 Message 屬性,則會輸出:

One or more errors occurred. (Test TestExceptionAsync)

await

使用 await 修飾符,發生異常的時候,拋出的不是 AggregateException 對象,而是 AggregateException 對象中的 InnerExceptions 屬性中找出第一個返回,隨意在使用 await 修飾符的場景下,捕獲異常的寫法是符合我們編程習慣的。

static?async?Task?Main(string[]?args) {try{await?TestExceptionAsync();}catch?(Exception?ex){Console.WriteLine(ex.Message);}Console.ReadLine(); }

希望本文對您有所幫助!

總結

以上是生活随笔為你收集整理的C# :异步编程的注意点的全部內容,希望文章能夠幫你解決所遇到的問題。

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