主要是为了看ET框架的ETTask这一节源码。所以来梳理一下C# async执行逻辑
参考文章:
https://www.cnblogs.com/pangjianxin/p/8710471.html
//www.greatytc.com/p/5dfbccac5c27
https://blog.csdn.net/weixin_43990579/article/details/105417652?spm=1001.2014.3001.5501
https://www.cnblogs.com/raytheweak/tag/C%23/ (这4篇有点难看,但是不得不看)
早些时候跑的测试代码://www.greatytc.com/p/b1d55ab05c44
标记了async编译起创建一个实现IAsyncStateMachine的状态机,既然是状态机那么肯定 有相应的状态,这里出现的状态有n+2个。n是await出现的次数。还有-1和-2。-1代表起始。-2代表完成状态。
可以被await的对象必须实现一个T GetAwaiter()方法、而T必须实现ICriticalNotifyCompletion(这个接口继承INotifyCompletion),bool IsCompleted()和TResult GetResult()方法。
具体流程(下面代码不是我的,也是我网上找的)
执行下面这方法
static async Task<string> MyMethodAsync(int argument)
{
var t1 = await GetType1();
var t2 = await GetType2();
return "Complete";
}
static async Task<Type> GetType1()
{
await Task.Run(() => { Console.WriteLine("GetType1"); });
return typeof(string);
}
static async Task<Type> GetType2()
{
await Task.Run(() => { Console.WriteLine("GetType2"); });
return typeof(int);
}
下面就是编译器生成的代码,网上有人叫骨架代码啥的
具体就是创建 一个状态机,初始化状态机的参数等
利用builder启动状态机(本质调用状态机的MoveNext方法)
[AsyncStateMachine(typeof(<MyMethodAsync>d__1))]
private static Task<string> MyMethodAsync(int argument)
{
/将方法实参拷贝到状态机的字段上
stateMachine = new <MyMethodAsync>d__1(); stateMachine.argument = argument;
/创建 builder
Task<string> stateMachine.t__builder = AsyncTaskMethodBuilder<string>.Create();
stateMachine.m_state = -1; // 设置状态的初始位置 // 开始执行状态机
/返回状态机的 Task
stateMachine.t__builder.Start(ref stateMachine)
; return stateMachine.t__builder.Task;
}
这是状态机, 状态机具体实现参考这篇文章->https://www.codercto.com/a/42846.html
private sealed class <MyMethodAsync>d__1 : IAsyncStateMachine
大致就讲了:
1、骨架函数中stateMachine.t__builder.Start(ref stateMachine)
。调用了MoveNext,状态机起始状态为-1
2、所以就转到了case -1:这个分支上了。这时第一个await后面的对象使用GetAwaiter()方法获得awaiter对象。然后通过这个awaiter对象的IsCompleted
来判断是否执行完成了。如果IsCompleted == false
。这时将会设置当前状态机的状态m_state为下一状态,保存当前awaiter用于将来返回。然后调用t__builder.AwaitUnsafeOnCompleted(ref awaiter1, ref stateMachine);
这个方法意思是任务完成后调用MoveNext重返状态机。
3、现在任务完成,重返状态机了,由2知道m_state,所以选择对应分支进入。拿到2中存的awaiter。利用awaiter.GetResult()获得计算结果。对于第二个await同样执行类似2的过程。最后跳转到最后分支获得第二个awaiter的结果。没有异常的话将MyMethodAsync函数结果通过t__builder.SetResult(result);返回出去。外部通过GetResult()得到结果
补充:
.Net中Task和Task<Result>是future类型(代表会在将来完成的操作)。Awaitable可等待对象
在设置完任务完成后的延续builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.Maind__0>(ref awaiter,ref stateMachine);后。此时将会根据awaiter的类型进行不同的情况有如下几种情况:
- TaskAwaiter 2、ConfiguredTaskAwaitable 3、ValueTask 4、Task-Like 5、不去捕捉上下文直接调用awaiter.UnsafeOnCompleted(box.MoveNextAction);
- Task默认会捕捉SynchronizationContext 不为null时将会Post。为null时(控制台程序SynchronizationContext = null)将会看看是否定义了TaskScheduler,如果没有就会被丢到线程池中