C# 异步编程中 await
实现原理详解
在C#中,async
和 await
关键字用于编写异步代码。本文将详细介绍 await
的实现原理,包括状态机的生成、回调函数的注册和触发等关键步骤。
1. 异步方法的基本概念
在C#中,async
关键字标记一个方法为异步方法,而 await
关键字用于等待一个异步操作完成。异步方法可以提高程序的响应性和性能,特别是在处理I/O操作和网络请求时。
2. 示例异步方法
我们以一个简单的异步方法为例,来详细解释 await
的实现原理。
public class Example { public async Task<int> CalculateAsync() { int a = await Task.Run(() => 10); int b = await Task.Run(() => 20); return a + b; } }
3. 编译器生成的状态机
编译器会为每个异步方法生成一个状态机。状态机是一个结构体,包含了异步方法的所有局部变量和状态信息。
编译器生成的状态机类
public class Example { public Task<int> CalculateAsync() { <CalculateAsync>d__0 stateMachine = new <CalculateAsync>d__0(); stateMachine.<>4__this = this; stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create(); stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } [StructLayout(LayoutKind.Auto)] [AsyncMethodBuilder(typeof(AsyncTaskMethodBuilder<int>))] private struct <CalculateAsync>d__0 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder<int> <>t__builder; public Example <>4__this; public int <a>5__1; public TaskAwaiter<int> <>u__1; private void MoveNext() { int num = <>1__state; try { TaskAwaiter<int> awaiter; switch (num) { case 0: goto TR_0000; case 1: <>1__state = -1; awaiter = <>u__1; <>u__1 = default(TaskAwaiter<int>); goto TR_0001; case 2: <>1__state = -1; break; default: <>1__state = 0; awaiter = Task.Run<int>(() => 10).GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); return; } goto TR_0000; } TR_0000: <a>5__1 = awaiter.GetResult(); awaiter = Task.Run<int>(() => 20).GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 1); <>u__1 = awaiter; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); return; } TR_0001: int b = awaiter.GetResult(); int result = <a>5__1 + b; <>1__state = -2; <>t__builder.SetResult(result); } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); } } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } } }
4. 实现流程详解
初始化状态机
在 CalculateAsync
方法中,创建状态机实例 <CalculateAsync>d__0
。
<CalculateAsync>d__0 stateMachine = new <CalculateAsync>d__0(); stateMachine.<>4__this = this; stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create(); stateMachine.<>1__state = -1;
<>4__this
:指向当前实例,即Example
类的实例。<>t__builder
:创建AsyncTaskMethodBuilder<int>
实例,用于管理任务的生命周期。<>1__state
:初始化状态为-1
,表示方法尚未开始执行。
开始执行
调用 Start
方法开始执行异步方法。Start
方法会调用状态机的 MoveNext
方法。
stateMachine.<>t__builder.Start(ref stateMachine);
执行方法体
在 MoveNext
方法中,根据当前状态 <>1__state
执行相应的代码。
private void MoveNext() { int num = <>1__state; try { TaskAwaiter<int> awaiter; switch (num) { // 处理不同的状态 } } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); } }
遇到 await
遇到第一个 await
关键字时,调用 Task.Run(() => 10).GetAwaiter()
获取 Awaiter
对象。
awaiter = Task.Run<int>(() => 10).GetAwaiter();
- 检查
awaiter.IsCompleted
,如果任务已经完成,直接调用awaiter.GetResult()
获取结果。 - 如果任务未完成,记录当前状态
<>1__state
,保存awaiter
对象,并调用<>t__builder.AwaitUnsafeOnCompleted
注册回调。
if (!awaiter.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); return; }
注册回调
AwaitUnsafeOnCompleted
方法会注册一个回调,当任务完成时,回调会被触发。
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { awaiter.UnsafeOnCompleted(stateMachine.MoveNext); }
awaiter.UnsafeOnCompleted
方法注册一个回调函数,该回调函数会在任务完成时被触发。stateMachine.MoveNext
是一个委托,指向状态机的MoveNext
方法。
任务完成
当任务完成时,回调会被触发,重新调用 MoveNext
方法,恢复异步方法的执行。
public void OnCompleted(Action continuation) { task.ContinueWith(_ => continuation(), TaskScheduler.Default); }
继续执行
从上次暂停的地方继续执行方法体。
TR_0000: <a>5__1 = awaiter.GetResult(); awaiter = Task.Run<int>(() => 20).GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 1); <>u__1 = awaiter; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); return; }
- 遇到第二个
await
关键字时,重复上述步骤。
方法完成
当所有异步操作完成并计算出结果后,设置状态 <>1__state
为 -2
,表示方法已经完成。
int b = awaiter.GetResult(); int result = <a>5__1 + b; <>1__state = -2; <>t__builder.SetResult(result);
- 调用
<>t__builder.SetResult
设置任务的结果。 - 如果在执行过程中抛出异常,捕获异常并调用
<>t__builder.SetException
设置任务的异常。
catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); }
5. 深入理解 AsyncTaskMethodBuilder
AsyncTaskMethodBuilder
是一个辅助类,用于构建和管理异步方法的任务。它提供了以下方法:
Create
:创建一个新的AsyncTaskMethodBuilder
实例。Start
:开始执行异步方法,调用状态机的MoveNext
方法。AwaitUnsafeOnCompleted
:注册回调函数,当任务完成时触发回调。SetResult
:设置任务的结果。SetException
:设置任务的异常。
AsyncTaskMethodBuilder
的内部实现
AsyncTaskMethodBuilder
内部维护了一个 Task
对象,用于表示异步操作的结果。当异步方法完成时,SetResult
方法会设置任务的结果,SetException
方法会设置任务的异常。
public struct AsyncTaskMethodBuilder<TResult> { private Task<TResult> task; public static AsyncTaskMethodBuilder<TResult> Create() { return new AsyncTaskMethodBuilder<TResult>(new Task<TResult>()); } private AsyncTaskMethodBuilder(Task<TResult> task) { this.task = task; } public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { stateMachine.MoveNext(); } public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { awaiter.OnCompleted(stateMachine.MoveNext); } public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { awaiter.UnsafeOnCompleted(stateMachine.MoveNext); } public void SetResult(TResult result) { task.SetResult(result); } public void SetException(Exception exception) { task.SetException(exception); } public Task<TResult> Task => task; }
6. 异步方法的生命周期
异步方法的生命周期可以分为以下几个阶段:
- 初始化:创建状态机实例,初始化状态和任务构建器。
- 开始执行:调用
Start
方法开始执行异步方法。 - 执行方法体:在
MoveNext
方法中,根据当前状态执行相应的代码。 - 遇到
await
:检查任务是否完成,如果未完成则注册回调并暂停方法执行。 - 任务完成:回调被触发,重新调用
MoveNext
方法,恢复异步方法的执行。 - 方法完成:所有异步操作完成,设置任务的结果或异常。
7. 异步方法的优势
使用 async
和 await
编写的异步方法有以下优势:
- 提高响应性:异步方法不会阻塞主线程,应用程序可以继续响应用户的输入和其他事件。
- 提高性能:异步方法可以并发执行多个任务,充分利用系统资源。
- 简化代码:异步方法的代码结构类似于同步方法,易于理解和维护。
8. 异步方法的注意事项
尽管 async
和 await
提供了许多优势,但在使用时也需要注意以下几点:
- 避免
async void
:async void
方法主要用于事件处理程序,其他情况下应避免使用,因为它无法被等待,并且异常处理较为困难。 - 异常处理:异步方法中的异常会被包装在
AggregateException
中,需要特殊处理。 - 资源管理:异步方法中使用
using
语句时,需要注意Dispose
方法的调用时机。
9. 完整的流程图
为了更好地理解这个过程,可以用流程图来展示:
总结
通过上述详细的解释和示例代码,我们可以总结出以下几点:
- 异步方法的基本概念:
async
和await
关键字用于编写异步代码。 - 状态机的生成:编译器为每个异步方法生成一个状态机,包含所有局部变量和状态信息。
MoveNext
方法的执行:MoveNext
方法是状态机的核心,负责管理和执行异步操作。- 回调函数的注册和触发:
- 当遇到
await
关键字时,编译器会生成代码来检查任务是否已经完成。 - 如果任务未完成,注册回调并暂停方法执行。
- 当任务完成时,回调函数会被触发,重新调用状态机的
MoveNext
方法,从而恢复异步方法的执行。
- 当遇到
AwaitUnsafeOnCompleted
方法的作用:在任务完成时注册一个回调函数,回调函数会在任务完成后被触发,从而恢复异步方法的执行。
希望这些解释能帮助你更好地理解 await
实现原理。如果你还有任何疑问,请随时提问!
详情请看:
原帖详情
官网讲解连接
希望这篇文章对你有所帮助!如果你有任何进一步的问题或需要更多的细节,请告诉我。