要点:1.函数增加async之后,就会编译器就会帮我我们生成一个状态机代码
static async void BBB()
{
Console.WriteLine("123");
}
编译器帮助生成的
[AsyncStateMachine(typeof (Program.\u003CBBB\u003Ed__5))]
[DebuggerStepThrough]
private static void BBB()
{
Program.\u003CBBB\u003Ed__5 stateMachine = new Program.\u003CBBB\u003Ed__5();
stateMachine.\u003C\u003Et__builder = AsyncVoidMethodBuilder.Create();
stateMachine.\u003C\u003E1__state = -1;
stateMachine.\u003C\u003Et__builder.Start<Program.\u003CBBB\u003Ed__5>(ref stateMachine);
}
AsyncVoidMethodBuilder类型根据async函数返回值生成
private sealed class \u003CBBB\u003Ed__5 : IAsyncStateMachine
{
public int \u003C\u003E1__state;
public AsyncVoidMethodBuilder \u003C\u003Et__builder;
public \u003CBBB\u003Ed__5()
{
base.\u002Ector();
}
void IAsyncStateMachine.MoveNext()
{
int num = this.\u003C\u003E1__state;
try
{
Console.WriteLine("123");
}
catch (Exception ex)
{
this.\u003C\u003E1__state = -2;
this.\u003C\u003Et__builder.SetException(ex);
return;
}
this.\u003C\u003E1__state = -2;
this.\u003C\u003Et__builder.SetResult();
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}
直接调用状态机的movenext,程序就直接往下走了。
如果async函数中,带有await,则会根据await 得到的awaiter类型,生成对应的代码
static async void BBB()
{
await Task.Delay(1000);
Console.WriteLine("Hello, World!");
}
[AsyncStateMachine(typeof (Program.\u003CBBB\u003Ed__1))]
[DebuggerStepThrough]
private static void BBB()
{
Program.\u003CBBB\u003Ed__1 stateMachine = new Program.\u003CBBB\u003Ed__1();
stateMachine.\u003C\u003Et__builder = AsyncVoidMethodBuilder.Create();
stateMachine.\u003C\u003E1__state = -1;
stateMachine.\u003C\u003Et__builder.Start<Program.\u003CBBB\u003Ed__1>(ref stateMachine);
}
[CompilerGenerated]
private sealed class \u003CBBB\u003Ed__1 : IAsyncStateMachine
{
public int \u003C\u003E1__state;
public AsyncVoidMethodBuilder \u003C\u003Et__builder;
private TaskAwaiter \u003C\u003Eu__1;
public \u003CBBB\u003Ed__1()
{
base.\u002Ector();
}
void IAsyncStateMachine.MoveNext()
{
int num1 = this.\u003C\u003E1__state;
try
{
TaskAwaiter awaiter;
int num2;
if (num1 != 0)
{
awaiter = Task.Delay(1000).GetAwaiter();
if (!awaiter.IsCompleted)
{
this.\u003C\u003E1__state = num2 = 0;
this.\u003C\u003Eu__1 = awaiter;
Program.\u003CBBB\u003Ed__1 stateMachine = this;
this.\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.\u003CBBB\u003Ed__1>(ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = this.\u003C\u003Eu__1;
this.\u003C\u003Eu__1 = new TaskAwaiter();
this.\u003C\u003E1__state = num2 = -1;
}
awaiter.GetResult();
Console.WriteLine("Hello, World!");
}
catch (Exception ex)
{
this.\u003C\u003E1__state = -2;
this.\u003C\u003Et__builder.SetException(ex);
return;
}
this.\u003C\u003E1__state = -2;
this.\u003C\u003Et__builder.SetResult();
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine([Nullable(1)] IAsyncStateMachine stateMachine)
{
}
}
因此像上面的代码,BBB函数中,虽然用了async标记,但是在调用BBB函数的地方,是不能用await的,因为BBB是void,外层函数没东西可以等待,改为下面
static async Task BBB()
{
await Task.Delay(1000);
Console.WriteLine("Hello, World!");
}
这样外层调用函数可以await BBB()了,其实BBB函数已经两个Task类实例(一个是Task.Delay,一个是AsyncTaskMethodBuilder类提供的,BBB函数中的状态机代码没变)
private static Task BBB()
{
Program.\u003CBBB\u003Ed__1 stateMachine = new Program.\u003CBBB\u003Ed__1();
stateMachine.\u003C\u003Et__builder = AsyncTaskMethodBuilder.Create();
stateMachine.\u003C\u003E1__state = -1;
stateMachine.\u003C\u003Et__builder.Start<Program.\u003CBBB\u003Ed__1>(ref stateMachine);
return stateMachine.\u003C\u003Et__builder.Task;
}
这样当外层函数调用await BBB()时,返回的AsyncTaskMethodBuilder.Task中的awaiter没有完成,只有BBB函数中状态机完成,才会调用
this.\u003C\u003E1__state = -2;
this.\u003C\u003Et__builder.SetResult();
然后外层的await中状态机才能获取到BBB函数返回的Task完成的状态,继续执行下去。然后一层套一层继续下去,可以一直等待。
那么如果是带返回值的呢,如下:
static async Task<string> BBB()
{
await Task.Delay(1000);
Console.WriteLine("Hello, World!");
return "123";
}
生成核心的movenext代码如下:
void IAsyncStateMachine.MoveNext()
{
int num1 = this.\u003C\u003E1__state;
string result;
try
{
TaskAwaiter awaiter;
int num2;
if (num1 != 0)
{
awaiter = Task.Delay(1000).GetAwaiter();
if (!awaiter.IsCompleted)
{
this.\u003C\u003E1__state = num2 = 0;
this.\u003C\u003Eu__1 = awaiter;
Program.\u003CBBB\u003Ed__1 stateMachine = this;
this.\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.\u003CBBB\u003Ed__1>(ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = this.\u003C\u003Eu__1;
this.\u003C\u003Eu__1 = new TaskAwaiter();
this.\u003C\u003E1__state = num2 = -1;
}
awaiter.GetResult();
Console.WriteLine("Hello, World!");
result = "123";
}
catch (Exception ex)
{
this.\u003C\u003E1__state = -2;
this.\u003C\u003Et__builder.SetException(ex);
return;
}
this.\u003C\u003E1__state = -2;
this.\u003C\u003Et__builder.SetResult(result);
}
最重要的就是:this.\u003C\u003Et__builder.SetResult(result);
就是说状态机走完了,将后续函数要返回的值,先声明一个string result;
,状态走完,设置这个result值,然后传给builder的SetResult方法,内部会调用对应Task的方法来设置结果,这样外层函数await的时候,也能拿到这个结果。
总结:函数标记async后,就会生成状态机代码,而且状态机根据本函数返回值,生成对应的builder,内部可能包含一个可以被等待的对象(Task.GetAwaiter(),返回的对象实现了ICriticalNotifyCompletion接口就算可等待对象),这样外层才能调用await。
async函数内部,使用了await 来等待一个可等待对象时(比如Task),会在状态机中生成,对应的代码,并将awaiter与状态机的movenext函数关联起来,当有任务完成时,会调用awaier的代码来调用movenext代码,再次回到状态机中进行下一步。
ET框架中,实现了自己的ETTask(他本身实现了接口ICriticalNotifyCompletion,所以ETTask.GetAwaiter()返回自身就可以了,且通过接口实现了,状态机moveNext方法与ETTask关联)。同时也实现了ETTask自己的类Builder类ETAsyncTaskMethodBuilder。
内部封装了一个ETTvoid,用途就是使得在ETTASK同步调用中,等待一下自己,然后等待自己的async函数返回ETVoid,对应有AsyncETVoidMethodBuilder,ETTASK中调用Coroutine,会等待自身完成,但是没有后续代码,外层调用ETTASK的地方就不用await了,这样异常也不会丢失。
写的很概括,后面有空再排版与优化这块,先挖坑