你可以使用異步編程來避免你的應用程序的性能瓶頸并且加強總體的響應。然而,用傳統的技術來寫異步應用是復雜的,同時編寫,調試和維護都很困難。
VS2012介紹了簡單的方法,那就是異步編程,它在.Net Framework 4.5和Windows 運行時提供了異步支持。編譯器做了開發者以前做的困難的工作,而且你的應用保持了類似于異步代碼的邏輯結構。結果,你輕易地就獲得了所有異步編程的優勢。
異步對于可能阻塞的活動是至關重要的。例如當你的應用訪問Web的時候,訪問web資源有時有點慢或者延時,如果這樣一個活動在同步進程中阻塞了,整個應用就必須等待。在異步進程中,此應用可以繼續其他的工作,而不依賴于web資源直到這個可能阻塞的任務完成。
下表展示了異步編程提升響應的典型領域。陳列的來自Framework 4.5 和the Windows Runtime 的APIs包含了支持async編程的方法。
應用領域 | 包含異步方法的APIs |
Web 訪問 | HttpClient, SyndicationClient |
處理文件 | StorageFile, StreamWriter, StreamReader, xmlReader |
處理圖片 | MediaCapture, BitmapEncoder, BitmapDecoder |
WCF編程 | Synchronous and Asynchronous Operations |
對于訪問UI線程的應用,異步被證明是特別有價值的,因為所有Ui相關的活動通常共享一個線程。如果同步應用的任何一個進程被阻塞了,那么所有進程都被阻塞了。屆時你的應用停止了響應,你可能推斷它出錯了,然而它卻僅僅在等待。
當你使用異步方法的時候,應用會繼續響應UI。你可以調整或者最小化窗口,或者如果你不想等待應用完成,那就關了它。
基于異步的方法相當于在設計異步操作時,向可供你選擇的選項增加了自動的傳輸裝置。那就是說,你以更少的付出卻獲得了所有傳統異步編程的好處。
關鍵字async和await是異步編程的核心。通過使用這兩個關鍵字,可以使用.NET Framework 或者Windows Runtime的資源來創建異步方法,這就像創建同步方法一樣簡單。使用await和async定義的方法為異步方法。
下面是一個異步方法的例子。代碼中的所有你都應該看著很熟悉。
// 簽名中需要注意的三件事:// -方法有一個async修飾符. // - 返回值是 Task 或 Task<T>. // 這里返回一個Task<int>,因為return語句返回int類型// - 方法名以 "Async"結尾。async Task<int> accessTheWebAsync(){ // 先要添加 System.Net.Http引用來聲明 client. HttpClient client = new HttpClient(); // GetStringAsync 返回 Task<string>. 這意味著當你等待這個任務的時候,你將獲得一個字符串(urlContents)。 Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); //這里你可以處理任務,它不依賴來自GetStringAsync的字符串 DoIndependentWork(); // await 操作符延緩了AccessTheWebAsync. // - AccessTheWebAsync 直到 getStringTask完成才繼續執行。 // - 同時, 控制返回到 AccessTheWebAsync的調用者. // - 當getStringTask完成時,控制恢復. // - await 操作符然后檢索來自 getStringTask的字符串. string urlContents = await getStringTask; // return 語句表明返回一個整數. return urlContents.Length;}
如果AccessTheWebAsync在調用GetStringAsync 和等待完成之間沒有其他要處理的代碼,可以用下面一條單句簡化代碼。
string urlContents = await client.GetStringAsync();
下面總結了一些上面的異步方法的例子的特點:
理解異步編程最重要的事情是 控制流如何從一個方法移動到另一個方法。下圖帶你理解這個過程。
對應數字序號的解釋如下:
你可能想知道在哪里找到支持異步的像GetStringAsync的方法。.NET Framework 4.5包含了許多對async和await有效的成員。你可以通過“Async”后綴和Task或Task<TResult>的返回類型來識別這些成員。比如,System.IO.Stream類中,在同步方法CopyTo, Read和 Write的旁邊包含了很多像CopyToAsync, ReadAsync和WriteAsync的方法。
async方法規定為非阻塞操作。當等待的task運行的時候,async方法的await表達式不會阻塞當前線程。相反,該表達式注冊當前方法的剩余作為延續,并且返回控制權給async方法的調用者。
async和await關鍵字不會造成額外的線程被創建。async方法不會要求多線程是因為async方法沒有運行在它自己的線程上。該方法運行在當前的同步上下文上,只有該方法激活的時候才會在該線程上使用時間。你可以使用Task.Run()來將CPU受限的工作移動到后臺線程中,但是后臺線程不會幫助處理僅僅等待結果變成可利用的。
可以查看我的這篇博客Async and Await 異步和等待。
請查看我的這篇博客Async and Await 異步和等待。
按照慣例,給方法添加async修飾符,給方法名追加“Async”后綴。
下面的代碼來自WPF應用的MainWindow.xaml.cs。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using System.Windows.Shapes;// 添加 System.Net.Http的using指令和引用;using System.Net.Http;namespace AsyncFirstExample{ public partial class MainWindow : Window { // 給事件句柄添加 async標記,為的是可在方法體內使用 await . PRivate async void StartButton_Click(object sender, RoutedEventArgs e) { // 分別調用和await. //Task<int> getLengthTask = AccessTheWebAsync(); //// 可以在這里做一些獨立的工作. //int contentLength = await getLengthTask; int contentLength = await AccessTheWebAsync(); resultsTextBox.Text += String.Format("/r/nLength of the downloaded string: {0}./r/n", contentLength); } // 簽名中需要注意的三件事: // -方法有一個async修飾符. // - 返回值是 Task 或 Task<T>. // 這里返回一個Task<int>,因為return語句返回int類型 // - 方法名以 "Async"結尾。 async Task<int> AccessTheWebAsync() { // 先要添加 System.Net.Http引用來聲明 client. HttpClient client = new HttpClient(); // GetStringAsync 返回 Task<string>. 這意味著當你等待這個任務的時候,你將獲得一個字符串(urlContents)。 Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); //這里你可以處理任務,它不依賴來自GetStringAsync的字符串 DoIndependentWork(); // await 操作符延緩了AccessTheWebAsync. // - AccessTheWebAsync 直到 getStringTask完成才繼續執行。 // - 同時, 控制返回到 AccessTheWebAsync的調用者. // - 當getStringTask完成時,控制恢復. // - await 操作符然后檢索來自 getStringTask的字符串. string urlContents = await getStringTask; // return 語句表明返回一個整數. return urlContents.Length; } void DoIndependentWork() { resultsTextBox.Text += "Working . . . . . . ./r/n"; } }}
新聞熱點
疑難解答