基本原理
async ,await是C#语言中用于简化异步操作的语法糖,实际会由编译器将代码翻译生成状态机来
执行异步操作。
状态机 是一种数学模型,用于描述一个系统在不同状态之间的转换行为。它由一组状态和一组转换组成,在特定的输入条件下,系统从一个状态转换到另一个状态。
例如如下的异步方法
public async Task<string> FetchDataAsync(string url)
{
using (HttpClient client = new HttpClient())
{
string content = await client.GetStringAsync(url);
Console.WriteLine("执行成功!");
return content;
}
}
当编译器遇到Async符号时,会将整个方法,转换为状态机,反编译整理简化后相关代码如下:
public Task<string> FetchDataAsync(string url)
{
var stateMachine = new FetchDataStateMachine();
stateMachine.url = url;
stateMachine.builder = AsyncTaskMethodBuilder<string>.Create();
stateMachine.state = -1;
stateMachine.builder.Start(ref stateMachine);
return stateMachine.builder.Task;
}
private struct FetchDataStateMachine : IAsyncStateMachine
{
public int state;
public AsyncTaskMethodBuilder<string> builder;
public string url;
private HttpClient client;
private TaskAwaiter<string> awaiter;
private string result;
public void MoveNext()
{
try
{
if (state!=0)
{
client = new HttpClient();
var task = client.GetStringAsync(url);
awaiter = task.GetAwaiter();
//判断任务是否已经执行完毕,如果执行完,直接就返回结果了
if (!awaiter.IsCompleted)
{
state = 0;
//注册回调,当task执行完后再执行MoveNext方法
builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
}
//任务执行完毕后设置state
if (state == 0)
{
state = -1;
}
result = awaiter.GetResult();
//执行await 以后的代码
Console.WriteLine("执行成功!");
}
catch (Exception ex)
{
state = -2;
builder.SetException(ex);
}
finally
{
if(num<0&&client!=null)
{
client.Dispose();
}
}
state=-2;
builder.SetResult(result);
}
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
builder.SetStateMachine(stateMachine);
}
}
编译器会生成一个FetchDataStateMachine 状态机类, 继承自IAsyncStateMachine 接口,将异步方法中的相关变量放在状态机字段中;状态机中的一个重要方法是MoveNext(),用于处理状态变化时的对应操作。
翻译后的代码主要执行以下两个操作
- 初始化状态机 , 新增状态机,并初始化相关字段,设置初始状态state为-1,启动状态机。
- 执行MoveNext()方法,state!=0,执行任务,如果任务耗时短直接可以获取结果,则直接返回结果;耗时长则设置state字段为0,并通过AwaitUnsafeOnCompleted注册任务完成后的回调,当任务完成后会再执行MoveNext()方法,此时state 字段为0,方法从awaiter中获取执行结果,并执行原方法中await以后的后续代码并返回。
AsyncTaskMethodBuilder<T>中AwaitUnsafeOnCompleted方法
internal static void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref Task<TResult>? taskField)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
IAsyncStateMachineBox box = GetStateMachineBox(ref stateMachine, ref taskField);
AwaitUnsafeOnCompleted(ref awaiter, box);
}
internal static void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref Task<TResult>? taskField)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
IAsyncStateMachineBox box = GetStateMachineBox(ref stateMachine, ref taskField);
AwaitUnsafeOnCompleted(ref awaiter, box);
}
internal static void AwaitUnsafeOnCompleted<TAwaiter>(
ref TAwaiter awaiter, IAsyncStateMachineBox box)
where TAwaiter : ICriticalNotifyCompletion
{
if ((null != (object?)default(TAwaiter)) && (awaiter is ITaskAwaiter))
{
ref TaskAwaiter ta = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter); // relies on TaskAwaiter/TaskAwaiter<T> having the same layout
TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, continueOnCapturedContext: true);
}
else if ((null != (object?)default(TAwaiter)) && (awaiter is IConfiguredTaskAwaiter))
{
ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter ta = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter);
TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, (ta.m_options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0);
}
else if ((null != (object?)default(TAwaiter)) && (awaiter is IStateMachineBoxAwareAwaiter))
{
try
{
((IStateMachineBoxAwareAwaiter)awaiter).AwaitUnsafeOnCompleted(box);
}
catch (Exception e)
{
Threading.Tasks.Task.ThrowAsync(e, targetContext: null);
}
}
else
{
// The awaiter isn't specially known. Fall back to doing a normal await.
try
{
awaiter.UnsafeOnCompleted(box.MoveNextAction);
}
catch (Exception e)
{
Threading.Tasks.Task.ThrowAsync(e, targetContext: null);
}
}
}
AwaitUnsafeOnCompleted()方法根据awaiter 的类型注册对应的OnCompleted任务完成后的回调方法,也就是MoveNext()方法,其中状态机被封装在IAsyncStateMachineBox类型的box中。
整个状态机的执行流程如下图
参考内容
Async/Await deep dive: how dotnet manage asynchronous code
Async/Await在 C#语言中是如何工作的
深入理解async 和 await 理解
.NET 6.0 中的 await 原理浅析
async、await原理揭秘
NetCore高级系列文章04---async、await原理揭秘
.NET Task 揭秘(3)async 与 AsyncMethodBuilder - 黑洞视界 - 博客园
进阶篇:以IL为剑,直指async/await - Dev_Eric - 博客园