感謝Marco CAO指出的兩點錯誤,已作出修改與補充c++
.NET Framework4.5提供了針對異步函數語法糖,簡化了編寫異步函數的複雜度。編程
下面經過一個簡單的示例,介紹.NET Framework4.5對異步函數的支持。網絡
窗體頁面異步
窗體代碼async
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void btnGetText_Click(object sender, EventArgs e) { string urlString = string.Empty; using (HttpClient client = new HttpClient()) { //異步獲取「http://10.32.112.82/epms/」內容(線程返回)。用戶界面依舊能夠操做 urlString = await client.GetStringAsync(@"http://10.32.112.82/epms/"); } txtUrlString.Text = urlString; } }
說明ide
1.點擊獲取url,程序開始獲取url內容。(注意:這裏我寫的url是咱們公司內部的網址,必須經過代理才能夠訪問,因此確定會有延遲效果。)異步編程
2.雖然有延遲效果,可是窗體依舊能夠操做(在第一個textBox中能夠輸入內容)。代表窗體並無被阻塞,也就是說獲取url內容是異步操做。函數
3.一段時間後,你會發現窗體報錯,代表獲取url內容操做完畢,操做失敗。工具
講解測試
1.異步函數的關鍵字:async;等待異步結果的關鍵字:await
2.異步方法的調用,與獲取異步方法的結果,在同一個方法體內。這點極大了簡化異步方法維護的成本。(之前BeginInvoke()、EndInvoke(),起碼是2個方法,且不能在同一方法體內調用。下文異步函數簡化APM模型有介紹。)
3.await做用的方法表示:【進入該方法的線程】會返回線程池,程序會在此掛起等待返回結果(這裏須要注意,由於程序獲得返回結果再執行時,是從線程池中另起了一個線程在執行),同時【子線程】會執行await後面的方法。(這點是:async與await的核心)
async/await關鍵點:
1.有幾個async方法,就建立幾個【狀態機】。
2.【狀態機】的幾種狀態(-1:初始化;-2:方法體執行結束;0:第一個await方法;1:第二個await方法;。。。依次往下)
3.【狀態機】控制着異步方法執行流程(MoveNext方法)
經過上面的介紹,咱們知道【狀態機】控制着方法執行流程,下面咱們就來對【狀態機】如何控制方法執行流程一探究竟。
下面這段測試代碼,來自:CLR via C#(第4版)
示例代碼
private static async Task<string> MyMethodAsync(int argument) { int local = argument; try { Type1 result1 = await Method1Async(); for (int x = 0; x < 3; x++) { Type2 result2 = await Method2Async(); } } catch (Exception) { Console.WriteLine("Catch"); } finally { Console.WriteLine("Finally"); } return "Done"; } private static async Task<Type1> Method1Async() { string a = await Task.Run<string>(() => "Method1"); return new Type1(); } private static async Task<Type2> Method2Async() { string a = await Task.Run<string>(() => "Method2"); return new Type2(); } sealed class Type1 { } sealed class Type2 { }
用Reflector.exe反編譯後,獲得以下代碼(MyMethodAsync)
[CompilerGenerated] private struct <MyMethodAsync>d__9 : IAsyncStateMachine { // Fields public int <>1__state; public AsyncTaskMethodBuilder<string> <>t__builder; private object <>t__stack; private TaskAwaiter<Program.Type1> <>u__$awaitere; private TaskAwaiter<Program.Type2> <>u__$awaiterf; public int <local>5__a; public Program.Type1 <result1>5__b; public Program.Type2 <result2>5__d; public int <x>5__c; public int argument; // Methods private void MoveNext() { string str; try { bool flag = true; switch (this.<>1__state) { case -3: goto Label_01E1; case 0: case 1: break; default: this.<local>5__a = this.argument; break; } try { switch (this.<>1__state) { } try { TaskAwaiter<Program.Type1> awaiter; switch (this.<>1__state) { case 0: break; case 1: goto Label_0137; default: awaiter = Program.Method1Async().GetAwaiter(); if (awaiter.IsCompleted) { goto Label_00D7; } this.<>1__state = 0; this.<>u__$awaitere = awaiter; this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<Program.Type1>, Program.<MyMethodAsync>d__9>(ref awaiter, ref this); flag = false; return; } awaiter = this.<>u__$awaitere; this.<>u__$awaitere = new TaskAwaiter<Program.Type1>(); this.<>1__state = -1; Label_00D7: Program.Type1 introduced11 = awaiter.GetResult(); awaiter = new TaskAwaiter<Program.Type1>(); Program.Type1 type = introduced11; this.<result1>5__b = type; this.<x>5__c = 0; while (this.<x>5__c < 3) { TaskAwaiter<Program.Type2> awaiter3 = Program.Method2Async().GetAwaiter(); if (awaiter3.IsCompleted) { goto Label_0156; } this.<>1__state = 1; this.<>u__$awaiterf = awaiter3; this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<Program.Type2>, Program.<MyMethodAsync>d__9>(ref awaiter3, ref this); flag = false; return; Label_0137: awaiter3 = this.<>u__$awaiterf; this.<>u__$awaiterf = new TaskAwaiter<Program.Type2>(); this.<>1__state = -1; Label_0156: Program.Type2 introduced12 = awaiter3.GetResult(); awaiter3 = new TaskAwaiter<Program.Type2>(); Program.Type2 type2 = introduced12; this.<result2>5__d = type2; this.<x>5__c++; } } catch (Exception) { Console.WriteLine("Catch"); } } finally { if (flag) { Console.WriteLine("Finally"); } } str = "Done"; } catch (Exception exception) { this.<>1__state = -2; this.<>t__builder.SetException(exception); return; } Label_01E1: this.<>1__state = -2; this.<>t__builder.SetResult(str); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine param0) { this.<>t__builder.SetStateMachine(param0); } }
這裏我要說一句抱歉,這裏貼出的代碼可讀性不好(沒有關鍵字,沒有着色)。原本想截圖的,可是因爲反編譯的代碼很長,沒法在我顯示器中一屏顯示完,因此我也沒截圖。下面講解的時候,採用分段截圖來講明,但願你們看得明白。同時也推薦你們本身用Reflector.exe反編譯後,本身閱讀。
代碼閱讀
1.【狀態機】初始化(-1)
2.【狀態機】Start入口-》MoveNext方法
3.【狀態機】個數(3個async方法,因此3個狀態機)
4.核心代碼(MoveNext方法)
這段代碼的大體意思是:<>1__state初始狀態爲-1,進入default分支,判斷Method1Async()是否執行完畢,若未執行完畢,<>1__state設計爲0(表示返回第一個await對應的方法塊),調用了一個方法(紅框標註),而後return(線程返回線程池,函數掛起等待返回結果)。若執行完畢,【goto Label_00D7】正好就是獲取第一個await結果的代碼塊。
下面重點關注一下,紅框標註的代碼(AsyncTaskMethodBuilder<string>.AwaitUnsafeOnCompleted())作了什麼?
依舊須要藉助反編碼工具,查到這個調用的真實代碼,以下
第一句是關鍵,方法名稱:獲得完成時調用的委託。進入方法一探究竟
AsyncTaskMethodBuilder.GetCompletionAction()代碼以下
紅框的批註,action就是完成時調用的方法,進入要MoveNextRunner.Run()方法中。
真相大白,action就是狀態機的MoveNext()方法。
總結:AsyncTaskMethodBuilder<string>.AwaitUnsafeOnCompleted()作得事就是,當異步函數執行完畢後,回調狀態機的MoveNext()方法
APM編程代碼
static void Main(string[] args) { AsyncAPM(); } static void AsyncAPM() { Console.WriteLine("Main thread ID={0}", Thread.CurrentThread.ManagedThreadId); byte[] s_data = new byte[100]; FileStream fs = new FileStream(@"d:\1.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous); fs.BeginRead(s_data, 0, s_data.Length, ReadIsDone, fs); Console.WriteLine("主線程執行完畢"); Console.ReadLine(); } private static void ReadIsDone(IAsyncResult ar) { Thread.Sleep(5000); Console.WriteLine("ReadIsDone thread ID={0}", Thread.CurrentThread.ManagedThreadId); FileStream fs = (FileStream)ar.AsyncState; int bytesRead = fs.EndRead(ar); fs.Close(); Console.WriteLine("Number of bytes read={0}", bytesRead); }
這種編程模式有一個很大的缺點:若是我想【獲取異步返回的結果,而後打印出來】。那麼【獲取異步返回結果,而後打印出來】必須放在回調函數中,也就是另外一塊代碼中(上面的實例,獲取字節數,而後打印出來。就必須放在ReadIsDone函數中),這其實給閱讀代碼增添了難度(若是多個異步函數,閱讀代碼時就要在不少方法中來回跳轉)。固然你也能夠在一個方法中用EndInvoke()來作等待。這樣雖然也是異步,可是會出現主程序阻塞,等待異步返回結果。
異步函數簡化代碼
static void Main(string[] args) { SimplifyAsyncToAPM(); Console.ReadLine(); } static async void SimplifyAsyncToAPM() { int length = await AsyncToAPM(); Console.WriteLine("File length={0}", length); } static async Task<int> AsyncToAPM() { byte[] s_data = new byte[100]; FileStream fs = new FileStream(@"d:\1.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous); return await Task.Factory.FromAsync<byte[], int, int, int>(fs.BeginRead, fs.EndRead, s_data, 0, s_data.Length, fs); }
示例代碼(讀取網絡中一張圖片的字節數)
static void Main(string[] args) { Task<int> result = AsyncEvent(new Uri("http://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%9B%BE%E7%89%87&pn=0&spn=0&di=84991311930&pi=&rn=1&tn=baiduimagedetail&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=3063552411%2C3030228420&os=1484689785%2C3968535026&simid=0%2C0&adpicid=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=11&oriquery=&objurl=http%3A%2F%2Fwww.52ij.com%2Fuploads%2Fallimg%2F160317%2F1110104P8-4.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bcdt3_z%26e3Bv54AzdH3F25g2xtw5AzdH3F9cmaa0_z%26e3Bip4s&gsm=0")); Console.WriteLine(result.Result); } static async Task<int> AsyncEvent(Uri uri) { WebClient wc = new WebClient(); TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); wc.DownloadDataCompleted += (s, e) => { if (e.Cancelled) tcs.SetCanceled(); else if (e.Error != null) tcs.SetException(e.Error); else tcs.SetResult(e.Result.Count()); }; wc.DownloadDataAsync(uri); return await tcs.Task; }
感謝你們的耐心閱讀