C#之異步編程

1 異步編程的重要性

  C#5.0最重要的改進是提供了更強大的異步編程,C#5.0僅增長兩個關鍵字Async和Await,使用異步編程,方法調用是後臺運行(一般在線程和任務的幫助下),而且不會阻塞調用線程。編程

2 異步模式

  從.net1.0開始就提供了異步特性,並且.NET Framework的許多類都實現了一個或多個異步模式(委託也實現了異步模式)。由於在WIndows From和WPF中用異步模式更新界面比較複雜,因此在2.0中提供了基於事件的異步模式。如今在4.5中推出了另一種新的方式來實現異步編程:基於任務的異步模式。這種模式是基於4.0中新增的Task類型,和一個利用Async和Await關鍵字的編譯器功能。框架

2.1 同步調用

 1 WebClient wc = new WebClient(); 2 string s= wc.DownloadString("你的URL"); 異步

  運行該代碼,程序一直在等待DownloadString的完成,在這種狀況下使用異步很是有必要的。async

2.2異步模式

使用異步模式是進行異步調用的方式之一,實現異步模式定義BeginXXX和EndXXX方法。若是有一個同步方法DownloadStrring,異步方法將轉化成了兩個方法BeginDownloadString和EndDownloadString方法。BeginXXX方法接受同步方法的全部輸入的參數,而且還定義了一個AsyncCallBack參數,用於接收在異步方法執行完畢後的被調用的委託,該方法返回一個IAsyncResult,用於驗證調用是否已經完成,而且一直等待,直到方法的執行完畢。EndXXX方法同步方法的全部的輸出參數,並按照同步方法的返回類型來返回結果。WebClient類沒有實現異步模式,但能夠用HttpWebRequest來代替異步編程

1  HttpWebRequest req =(HttpWebRequest)WebRequest.Create("你的URL"); 2             IAsyncResult result=  req.BeginGetResponse("",); 3             req.EndGetResponse();

異步模式的優點是使用委託功能就能實現異步編程,不用改變程序的行爲,也不會阻塞界面的操做。spa

2.3基於事件的異步模式

  基於事件的異步模式定義了一個帶有"Aysnc"後綴的方法。異步方法完成時不是定義被調用的委託,而是定義事件。.net

1 WebClient client = new WebClient(); 2             client.DownloadStringCompleted += (sender, e1) => { }; 3             client.DownloadStringAsync(new Uri("你的URL"));

基於事件的異步方式的優點是易用。線程

2.4基於任務的異步模式

1 public async void Fun() 2  { 3             WebClient client = new WebClient(); 4             string s = await client.DownloadStringTaskAsync("你的URL"); 5         }

如今代碼簡單多了,而且沒有阻塞。code

3 異步編程的基礎

  async和await關鍵字只是編譯器功能,編譯器會用Task建立代碼。orm

3.1建立任務

1         static string Greeting(string name) 2  { 3             Thread.Sleep(2000); 4             return string.Format("Hello ,{0}", name); 5  } 6         static Task<string> GreetingAsync() 7  { 8             return Task.Run<string>(()=> { return Greeting(name); }); 9         }

 

3.2調用異步方法

  可使用Await關鍵字來調用返回任務的異步方法GreetingAsync,使用Await關鍵字須要使用Async修飾聲明的方法。在完成GreetingAsync方法前該方法內的其它代碼不會被執行。

1   private async static void CallWithAsync() 2  { 3             string result = await GreetingAsync("鄭隆"); 4  Console.WriteLine(result); 5         }

若是異步方法的結果不傳遞給變量,也能夠直接在參數中使用Await關鍵字。

1  private async static void CallWithAsync2() 2  { 3             Console.WriteLine(await GreetingAsync("鄭隆")); 4         }
Async修飾符只能用於返回Task和void的方法。不能用於程序的入口。

3.3延續任務

Task類的ContinueWith方法定義了任務完成後將要調用的代碼。

1    private static void CallerWithContinuationTask() 2  { 3             Task<string> t1 = GreetingAsync("鄭隆"); 4             t1.ContinueWith(t => { string result = t.Result; Console.WriteLine(result); }); 5         }

 

3.4使用多個異步方法

在一個異步方法中,能夠調用不止一個異步方法。

1  public async static void MultipleAsyncMethods() 2  { 3             string s1 = await GreetingAsync("zhenglong1"); 4             string s2 = await GreetingAsync("zhenglong2"); 5             Console.WriteLine("Finished both methods.\n"+"Result 1:{0}\n Result 2:{1}",s1,s2); 6         }

Task.WhenAll組合器,可讓你等待直到兩個任務都完成。

1  public async static void MultipleAsyncMethodsWithCombinators1() 2  { 3             Task<string>  t1= GreetingAsync("zhenglong1"); 4             Task<string> t2 = GreetingAsync("zhenglong2"); 5             await Task.WhenAll(t1,t2); 6             Console.WriteLine("Finished both methods.\n" + "Result 1:{0}\n Result 2:{1}", t1.Result, t2.Result); 7         }

Task的WhenAll方法是在全部傳入的任務都完成了才返回Task,而WhenAny是在其中的一個任務已完成就會返回Task。

3.5轉換異步模式

並不是.NET Freamwork的全部的類在4.5中都引入了新的異步方法。

 1 private static Func<string, string> greetingInvoker = Greeting;  2 
 3         static IAsyncResult BeginGreeting(string name,AsyncCallback callback,object state)  4  {  5             return greetingInvoker.BeginInvoke(name, callback, state);  6  }  7         static string EndGreeting(IAsyncResult ar)  8  {  9            return greetingInvoker.EndInvoke(ar); 10  } 11  private async static void ConvertingAsyncPattern() 12  { 13             string s = await Task<string>.Factory.FromAsync(BeginGreeting, EndGreeting, "zhenglong", null); 14  Console.WriteLine(s); 15         }

4 錯誤處理

 1   public static async Task ThrowAfter(int ms,string message)  2  {  3             await Task.Delay(ms);  4             throw new Exception(message);  5  }  6 
 7         public static void DontHandle()  8  {  9             try
10  { 11                 ThrowAfter(200,"first"); 12  } 13             catch (Exception ex) 14  { 15  Console.WriteLine(ex.Message);17  } 18         }

以上代碼並不能捕獲到異常,應爲在ThrowAfter拋出異常以前DontHandle已經執行完了。

4.1 異步方法的異常處理

異步方法異常的一個較好的處理是使用關鍵字Await,而後將其放在try中

 1    public async static void HandleErrorOne()  2  {  3             try
 4  {  5                 await ThrowAfter(200, "first");  6  }  7             catch (Exception ex)  8  {  9  Console.WriteLine(ex.Message); 10  } 11         }

 

4.2 多個異步方法的異常處理

 1   public static async void StatTwoTasks()  2  {  3             try
 4  {  5                 await ThrowAfter(2000,"frist");  6                 await ThrowAfter(1000, "second");  7  }  8             catch (Exception ex)  9  { 10  Console.WriteLine(ex.Message); 11  } 12         }

如上代碼並不能捕獲所有的異常,緣由是由於在第一個異常拋出後程序就進入了catch。解決方法

 1  public static async void StatTwoTasksParallel()  2  {  3             try
 4  {  5                 Task t1 = ThrowAfter(200, "zhenglong1");  6                 Task t2 = ThrowAfter(200, "zhenglong2");  7                 await Task.WhenAll(t1,t2);  8  }  9             catch (Exception ex) 10  { 11  Console.WriteLine(ex.Message); 12  } 13         }

 

4.3 AggregateException類

爲了獲得全部的異常信息,能夠將Task.WhenAll返回的結果寫到一個Task中,這個任務一直等待全部的任務完成。

 1 public static async void ShowAggregateException()  2  {  3             Task taskResult = null;  4             try
 5  {  6                 Task t1 = ThrowAfter(200, "zhenglong1");  7                 Task t2 = ThrowAfter(200, "zhenglong2");  8                 await(taskResult= Task.WhenAll(t1, t2));  9  } 10             catch (Exception ex) 11  { 12  Console.WriteLine(ex.Message); 13                 foreach (var item in taskResult.Exception.InnerExceptions) 14  { 15  Console.WriteLine(item.Message); 16  } 17  } 18         }

5取消

在某種狀況下,後臺任務可能運行很長的時間,取消任務就很是有用了。

5.1 開始取消任務

1 private CancellationTokenSource cts; 2         public void Cancle() 3  { 4             if (cts != null) 5  cts.Cancel(); 6         }
CancellationTokenSource 類還支持在指定時間後才取消任務,CancelAfter方法在應該取消的任務後傳入一個時間值,單位是ms.

5.2開始框架的特性任務取消

1  public async void CancelTwo() 2  { 3             cts = new CancellationTokenSource(); 4             HttpClient hc = new HttpClient(); 5             var s=await hc.GetAsync("Url", cts.Token); 6         }

5.3取消自定義任務

1  await Task.Run(()=> { },cts.Token);

如今用戶就能夠取消運行時間長的任務了。

相關文章
相關標籤/搜索