異步編程是C#5.0的一個重要改進,提供兩個關鍵字:async和await。使用異步編程,方法的調用是在後臺運行(一般在線程或任務的幫助下),但不會阻塞調用線程。異步模式分爲3種:異步模式、基於事件的異步模式和基於任務的異步模式(TAP)。TAP是利用關鍵字async和await實現的,本文將講解TAP模式。async和await關鍵字只是編譯器的功能。編譯器最終會用Task類建立代碼。html
創建一個同步方法Greeting,該方法在等待一段時間後,返回一個字符串。編程
private string Greeting(int delay, string name) { System.Threading.Thread.Sleep(delay); return string.Format("Hello, {0}.", name); }
定義一個方法GreetingAsync,能夠使方法異步化,其傳入的參數不作強制要求。基於任務的異步模式指定,並返回一個任務。注意,該方法返回的是Task<string>,定義了一個返回字符串的任務,與同步方法返回值一致。數組
private Task<string> GreetingAsync(string name, int delay = 3000) { return Task.Run<string>(() => { return Greeting(delay, name); }); }
能夠使用await關鍵字調用返回任務的異步方法GreetingAsync。可是,使用await關鍵字的方法必需要用async關鍵字修飾符聲明。在GreetingAsync方法完成前,被async關鍵字修飾的方法內await關鍵字後面的代碼不會繼續執行。可是,啓動被async關鍵字修飾的方法的線程能夠被重用,而沒有被阻塞。異步
public async void CallerWithAsync() { string result = await GreetingAsync("Nigel", 2000); Console.WriteLine(result); }
GreetingAsync方法返回一個Task<string>對象。該對象包含任務建立的信息,並保存到任務完成。Task類的ContinueWith方法可在任務完成後繼續調用的代碼。async
public void CallsWithContinuationTask() { Task<string> task = GreetingAsync("Stephanie", 1000); task.ContinueWith(t => { Console.WriteLine(t.Result); }); }
實際上,編譯器會把await關鍵字後的全部代碼放進ContinueWith方法內。不管是await關鍵字的方法仍是任務的ContinueWith方法,在方法的不一樣生命階段使用了不一樣的線程。都是當await關鍵字的方法或任務執行完畢後,再由另外一個線程去執行await關鍵字後面的代碼,或給當前線程添加新的任務去執行相關代碼。異步編程
在具備UI的應用程序中,應用程序的窗體的控件不容許跨線程訪問,須要使用控件的InvokeRequired屬性和Invoke方法,將訪問UI的方法代碼塊以委託的形式傳遞給控件的Invoke,可是在執行前須要判斷控件的InvokeRequired。在使用async和await關鍵字,當await完成後,不須要作任何處理,就能夠放訪問UI線程(其實是將控制權又交給了UI線程)。post
使用await關鍵字能夠調用每一個異步方法。若是一個異步方法依賴於另外一個異步方法,將會起到很大做用。但當異步方法之間沒有相互依賴的時候,不使用await關鍵字將更快返回結果。ui
public async void MultipleAsyncMethods() { DateTime start = DateTime.Now; string result1 = await GreetingAsync("Jack",2500);//先執行完它 string result2 = await GreetingAsync("Tim",1500);//再執行它 //輸出結果 Console.WriteLine("Finished both methods: MultipleAsyncMethods.\nResult 1: {0}, Result 2: {1}", result1, result2); Console.WriteLine("Use time: {0}", (DateTime.Now - start).TotalMilliseconds); }
若是任務之間並不依賴於另外一個任務,每一個異步方法都不須要使用await,而是把每一個異步方法的返回結果賦值給Task變量,使用組合器讓這些任務並行運行。當組合器內的全部任務都完成後,纔會執行後面的代碼。url
public async void MultipleAsyncMethodsWithCombinators1() { DateTime start = DateTime.Now; Task<string> t1= GreetingAsync("Jack", 2500); Task<string> t2= GreetingAsync("Tim", 1500); await Task.WhenAll(t1, t2); //輸出結果 Console.WriteLine("Finished both methods: MultipleAsyncMethodsWithCombinators1.\nResult 1: {0}, Result 2: {1}", t1.Result, t2.Result); Console.WriteLine("Use time: {0}", (DateTime.Now - start).TotalMilliseconds); }
若是全部任務類型都返回相同的類型,則可用該類型的數組做爲await返回的結果spa
public async void MultipleAsyncMethodsWithCombinators2() { DateTime start = DateTime.Now; Task<string> t1 = GreetingAsync("Jack", 2500); Task<string> t2 = GreetingAsync("Tim", 1500); string[] results= await Task.WhenAll(t1, t2); //輸出結果 Console.WriteLine("Finished both methods: MultipleAsyncMethodsWithCombinators2.\nResult 1: {0}, Result 2: {1}", results[0], results[1]); Console.WriteLine("Use time: {0}", (DateTime.Now - start).TotalMilliseconds); }
若是調用異步方法,可是沒有等待,那麼調用異步方法的線程中使用傳統的try/catch塊是不能捕獲到異步方法中的異常。由於在異步方法執行出現異常以前,已經執行完畢。
如何捕獲異常見《基於任務的異步編程模式(TAP)的錯誤處理》。