NetCore高级系列文章04---async、await原理揭秘
一、async、await本质
直接说结论:它们是C#提供的语法糖,编译器编译后是状态机的调用。
先看如下的一段代码,要main方法中调用了三个await方法
将此dll进行反编译为4.0的代码如下:
可见到两个Main方法,也就是说我们在程序中Main方法上加了async关键词,编译器会编译成一个是异步的一个是非异步方法,程序还是将非异步的方法作为入口函数。进入函数
在该函数中调了异步的Main方法,再进入:
在该函数中创建了一个状态机,将参数传给状态机,并调用期Start方法,可知异步方法实际上是状态机方法的调用
进入状态机类型<Main>d__0
private sealed class <Main>d__0 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder <>t__builder; public string[] args; private string <text>5__1; private HttpClient <httpClient>5__2; private string <html>5__3; private string <>s__4; private string <>s__5; private TaskAwaiter<string> <>u__1; private TaskAwaiter <>u__2; private void MoveNext() { int num = <>1__state; try { TaskAwaiter awaiter2; TaskAwaiter<string> awaiter; switch (num) { default: <httpClient>5__2 = new HttpClient(); goto case 0; case 0: try { TaskAwaiter<string> awaiter3; if (num != 0) { awaiter3 = <httpClient>5__2.GetStringAsync("https://www.baidu.com").GetAwaiter(); if (!awaiter3.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter3; <Main>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine); return; } } else { awaiter3 = <>u__1; <>u__1 = default(TaskAwaiter<string>); num = (<>1__state = -1); } <>s__4 = awaiter3.GetResult(); <html>5__3 = <>s__4; <>s__4 = null; Console.WriteLine(<html>5__3); <html>5__3 = null; } finally { if (num < 0 && <httpClient>5__2 != null) { ((IDisposable)<httpClient>5__2).Dispose(); } } <httpClient>5__2 = null; awaiter2 = File.WriteAllTextAsync("E:\\test.txt", "zhengwei").GetAwaiter(); if (!awaiter2.IsCompleted) { num = (<>1__state = 1); <>u__2 = awaiter2; <Main>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } goto IL_015f; case 1: awaiter2 = <>u__2; <>u__2 = default(TaskAwaiter); num = (<>1__state = -1); goto IL_015f; case 2: { awaiter = <>u__1; <>u__1 = default(TaskAwaiter<string>); num = (<>1__state = -1); break; } IL_015f: awaiter2.GetResult(); Console.WriteLine("写入成功"); awaiter = File.ReadAllTextAsync("E:\\test.txt").GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 2); <>u__1 = awaiter; <Main>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } break; } <>s__5 = awaiter.GetResult(); <text>5__1 = <>s__5; <>s__5 = null; Console.WriteLine("文件内容" + <text>5__1); } catch (Exception exception) { <>1__state = -2; <text>5__1 = null; <>t__builder.SetException(exception); return; } <>1__state = -2; <text>5__1 = null; <>t__builder.SetResult(); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } }
在此状态机类中可以看到我们自己写的Main方法中的主要代码已编译到了方法MoveNext()中,并将我们的局部变量编译成了成员变量
方法中有一个case,我们自己写的三个异步方法被编译后拆到了多个case中去了,MoveNext方法会根据不同的num被多次调用
编译的代码中可见到在每次调用异步方法时都会去判断是否完成
编译后的代码意思是当执行到string html = await httpClient.GetStringAsync("https://www.baidu.com");时,如果没有完成就直接返回了,并不会再往下执行
只有当此段代码执行完成后会继续往下执行,当执行到第二个异步方法await File.WriteAllTextAsync(@"E:\test.txt","zhengwei")时又会返回,等待执行完后再往下执行
同理,执行第三步异步方法var text = await File.ReadAllTextAsync(@"E:\test.txt");也是如此
所有异步方法都 执行完成后,会break;跳出状态机。
未完待续。。。
二、async背后的线程切换
await调用的等待期间,.net会把当前的线程返回线线程池,等异步方法调用执行完毕后,框架会从线程池再取出来一个线程执行后续的代码。
代码验证
运行的结果可知在这个方法中使用的并不是同一个进程,一个是1,一个是9
当然,有时写入的内容少,也有可能方法开始和结束的线程id是一样的,
三、异步方法与多线程的关系
异步方法的代码并不会自动在新的线程中执行,除非把代码放到新线程中执行。
在方法调用前和方法调用中用的是同一个线程,都是1,并没有开启一个新的线程
除非把这个异步方法放到一个新的线程中:可以看到方法调用中的线程为4,调用前的线程为1。
四、CancellationToken在异步方法中的使用