一、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在异步方法中的使用