在.NET異步編程中,通常使用async和await這對黃金搭檔,返回類型使用Task或Task<T>。在方法前面加async表示這個方法運行異步,在方法內使用await表示執行一個異步等待。
下面是一個簡單例子:
static void Main(string[] args){Doth();Console.ReadKey();}static async Task Doth(){int i = 2;await Task.Delay(TimeSpan.FromSeconds(2));i += 2;await Task.Delay(TimeSpan.FromSeconds(2));Console.WriteLine(i);}
當執行DoSth方法,第一個await執行一個異步等待,當執行完成,就繼續執行下面的代碼。在async修飾的方法內部,一個await就是一個異步等待,可以包含多個await, 每一個await執行完畢才會執行它後面的代碼,也就是說在DoSth內部是同步的。
各個await在哪個線程中運行呢?
默認情況下是在當前線程中運行,不過.NET提供了ConfigureAwait方法,用來設置await在哪個線程中運行。
static async Task Doth(){int i = 2;await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);i += 2;await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);Console.WriteLine(i);}
以上,當第一個await運行在控制台項目所在的線程中,第二個await將在線程池上運行。
我們無法保證每個await不會拋出異常,通常按如下的方式捕獲異常。
async Task DosthAsync(){try{await PossibleExceptionAsync();}catch(NotSuppotedException ex){LogException(ex);throw;}}
由於拋出的異常會放在Task對象中,所以也可以這麼寫:
async Task DosthAsync(){Task task = PossibleExceptionAsync();try{await task;}cach(NotSupportedException ex){LogException(ex);throw;}}
異步編程也會出現死鎖。
async Task DoSthSync(){await Task.Delay(TimeSpan.FromSeconds(1));}void FirstThing(){Task tak = DoSthSync();task.Wait();}
以上,如果調用FirstThing方法就會出現死鎖的情況。
→執行FirstThing方法
→在FirstThing方法內部執行DoSthSync方法,這時,當前上下文線程被阻塞
→來到DoSthSync方法中,其中的await試圖捕獲當前上下文線程,而當前的上下線程已經被阻塞在那裡了,造成死鎖。
死鎖如何解決死鎖呢?
可以在DoSthSync內部不使用當前上下文線程,改用線程池中的線程,修改如下:
async Task DoSthSync(){await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);}
或者,讓FirstThing變成一個異步方法。修改如下:
async Task DoSthSync(){await Task.Delay(TimeSpan.FromSeconds(1));}async Task FirstThing(){await DoSthSync();}