异步函数(async/await)简单应用
.NET Framework4.5提供了针对异步函数语法糖,简化了编写异步函数的复杂度。
下面通过一个简单的示例,介绍.NET Framework4.5对异步函数的支持。
示例代码
class Program { static void Main(string[] args) { EasyAsyncAwait(); Console.ReadLine(); } static async void EasyAsyncAwait() { //① Task<string> task = GetName(); //③ Console.WriteLine("DoSomeThing"); //⑤ Console.WriteLine("Result:{0}", await task); } //② private static async Task<string> GetName() { //④ return await Task.Run(() => { Thread.Sleep(5000); return "Test"; }); } }
说明
1.异步函数的关键字:async;获取异步结果的关键字:await
2.代码的执行顺序已经标注,主要关注②、③。②执行后代码立刻返回执行③,然后当⑤需要访问结果时,等待④执行完毕返回结果。
3.异步方法的调用,与获取异步方法的结果,在同一个方法体内。这点极大了简化异步方法维护的成本。(以前BeginInvoke()、EndInvoke(),起码是2个方法,且不能在同一方法体内调用)
4.当代码运行到async,程序会判断异步方法是否执行完毕。如果执行完毕,获取结果,代码继续往下运行。如果未执行完毕,线程返回(可以处理系统安排的别的任务),记录下当前位置(以便异步方法执行完毕后,合适的线程继续调用未执行的代码),代码不继续运行直到异步函数返回结果。(这点绝对是重中之重,理解了这点,会解决心中对async/await的疑惑)
异步函数(async/await)实现异步原理(状态机)
async/await实现异步原理:当执行代码遇到async时,知道该方法为异步方法,然后开始检查该方法体内一共有多少个await。如果await的个数>0,那么就创建状态机,来维护异步方法线程的返回与异步方法执行完毕之后的结果调用。(以上是我个人的理解,如有错误请各位指正,谢谢)
通过上面的介绍,我们知道状态机在异步方法线程的返回与结果的调用中起着至关重要的作用,下面我们就来对状态机一探究竟。
下面这段测试代码,来自: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.字段
这里我们最要关注的字段就是:int <>1__state(初始状态-1),这个字段控制着异步方法当前的状态(未执行、执行完毕),并且负责跳转到对应的异步方法(如果有多个await,就由它负责切换执行对应的代码)
2.核心代码
这段代码的大致意思是:<>1__state初始状态为-1,进入default分支,判断Method1Async()是否执行完毕,若未执行完毕,<>1__state设计为0(表示执行完毕),调用了一个方法(红框标注),然后return(线程返回)。
下面重点关注一下,红框标注的代码(AsyncTaskMethodBuilder<string>.AwaitUnsafeOnCompleted())做了什么?
依旧需要借助反编码工具,查到这个调用的真实代码,如下
第一句是关键,方法名称:得到完成时调用的委托。进入方法一探究竟
AsyncTaskMethodBuilder.GetCompletionAction()代码如下
红框的批注,action就是完成时调用的方法,进入要MoveNextRunner.Run()方法中。
真相大白,action就是状态机的MoveNext()方法。
总结:AsyncTaskMethodBuilder<string>.AwaitUnsafeOnCompleted()做得事就是,当异步函数执行完毕后,回调状态机的MoveNext()方法
异步函数(async/await)简化APM编程模型
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); }
异步函数简化代码
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); }
异步函数(async/await)应用于事件编程模型
示例代码(读取网络中一张图片的字节数)
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; }
感谢大家的耐心阅读