C# 异步编程中 await 实现原理详解

在C#中,asyncawait 关键字用于编写异步代码。本文将详细介绍 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. 异步方法的生命周期

异步方法的生命周期可以分为以下几个阶段:

  1. 初始化:创建状态机实例,初始化状态和任务构建器。
  2. 开始执行:调用 Start 方法开始执行异步方法。
  3. 执行方法体:在 MoveNext 方法中,根据当前状态执行相应的代码。
  4. 遇到 await:检查任务是否完成,如果未完成则注册回调并暂停方法执行。
  5. 任务完成:回调被触发,重新调用 MoveNext 方法,恢复异步方法的执行。
  6. 方法完成:所有异步操作完成,设置任务的结果或异常。

7. 异步方法的优势

使用 asyncawait 编写的异步方法有以下优势:

  • 提高响应性:异步方法不会阻塞主线程,应用程序可以继续响应用户的输入和其他事件。
  • 提高性能:异步方法可以并发执行多个任务,充分利用系统资源。
  • 简化代码:异步方法的代码结构类似于同步方法,易于理解和维护。

8. 异步方法的注意事项

尽管 asyncawait 提供了许多优势,但在使用时也需要注意以下几点:

  • 避免 async voidasync void 方法主要用于事件处理程序,其他情况下应避免使用,因为它无法被等待,并且异常处理较为困难。
  • 异常处理:异步方法中的异常会被包装在 AggregateException 中,需要特殊处理。
  • 资源管理:异步方法中使用 using 语句时,需要注意 Dispose 方法的调用时机。

9. 完整的流程图

为了更好地理解这个过程,可以用流程图来展示:

总结

通过上述详细的解释和示例代码,我们可以总结出以下几点:

  1. 异步方法的基本概念asyncawait 关键字用于编写异步代码。
  2. 状态机的生成:编译器为每个异步方法生成一个状态机,包含所有局部变量和状态信息。
  3. MoveNext 方法的执行MoveNext 方法是状态机的核心,负责管理和执行异步操作。
  4. 回调函数的注册和触发
    • 当遇到 await 关键字时,编译器会生成代码来检查任务是否已经完成。
    • 如果任务未完成,注册回调并暂停方法执行。
    • 当任务完成时,回调函数会被触发,重新调用状态机的 MoveNext 方法,从而恢复异步方法的执行。
  5. AwaitUnsafeOnCompleted 方法的作用:在任务完成时注册一个回调函数,回调函数会在任务完成后被触发,从而恢复异步方法的执行。

希望这些解释能帮助你更好地理解 await 实现原理。如果你还有任何疑问,请随时提问!

详情请看:https://www.cnblogs.com/Bob-luo/p/18518463

希望这篇文章对你有所帮助!如果你有任何进一步的问题或需要更多的细节,请告诉我。

深入解析C#异步编程:await 关键字背后的实现原理的更多相关文章

  1. C#异步编程のawait和async关键字来写异步程序

    一.await和async关键字 .Net平台不断推出了新的异步编程模型,在.net4.5中加入了关键字await和async,顾名思义,await是指方法执行可等待,即可挂起直到有结果(不是必须立即 ...

  2. 深入解析js异步编程利器Generator

    我们在编写Nodejs程序时,经常会用到回调函数,在一个操作执行完成之后对返回的数据进行处理,我简单的理解它为异步编程. 如果操作很多,那么回调的嵌套就会必不可少,那么如果操作非常多,那么回调的嵌套就 ...

  3. 深入解析Javascript异步编程

    这里深入探讨下Javascript的异步编程技术.(P.S. 本文较长,请准备好瓜子可乐 :D) 一. Javascript异步编程简介 至少在语言级别上,Javascript是单线程的,因此异步编程 ...

  4. .Net 多线程 异步编程 Await、Async和Task

    await和async简介   await和async是在C#5中引入,并且在.NetFramewor4.5以及.NetCore中进行了支持.主要是解决性能瓶颈,并且增强系统的响应能力. msdn关于 ...

  5. 一步步疑难解析 —— Python 异步编程构建博客

    声明:该项目学习资源主要来自廖雪峰的Python教程,参见 http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6 ...

  6. 对于异步编程Await和Async的理解

    public class AsyncInSync { /// <summary> /// 同步代码里有异步代码 /// /// /// 结果 /// Main Thread Before ...

  7. 基于纤程(Fiber)实现C++异步编程库(一):原理及示例

    纤程(Fiber)和协程(coroutine)是差不多的概念,也叫做用户级线程或者轻线程之类的.Windows系统提供了一组API用户创建和使用纤程,本文中的库就是基于这组API实现的,所以无法跨平台 ...

  8. 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

    一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ...

  9. 转:[你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单

    本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单  async和await关键字剖析 小结 一.引言 在之前的C#基础知 ...

  10. [你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单

    本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单  async和await关键字剖析 小结 一.引言 在之前的C#基础知 ...

随机推荐

  1. CH03_运算符

    CH03_运算符 算术运算符 作用:用于处理四则运算 示例: #include <iostream> using namespace std; int main() { int a = 1 ...

  2. 变分信息瓶颈 (Variational Information Bottleneck) 公式推导

    互信息 互信息用于表示两个随机变量相互依赖的程度.随机变量 \(X\) 和 \(Y\) 的互信息定义为 \[\begin{aligned} I(X, Y) & = \mathrm{KL}[p( ...

  3. 蓝松SDK版本更新至【4.8.0】

    [Android更新] 1. android增加低精度模型,一些低端机也可以流畅抠图. 2. android针对直播机硬件做了特定API,并C语言开源. 3. 视频编辑引出帧解码图层, 允许外界异步形 ...

  4. 关于腾讯会议pc端的使用教程

    一.首先先在官网下载pc端 下面是连接:腾讯会议_腾讯会议下载- 腾讯云 (tencent.com) 点击免费下载即可. 二.登录并创建会议 选择一种登录方式. 创建会议有快速会议和预定会议两种方式 ...

  5. 使用 nuxi info 查看 Nuxt 项目详细信息

    title: 使用 nuxi info 查看 Nuxt 项目详细信息 date: 2024/9/5 updated: 2024/9/5 author: cmdragon excerpt: 摘要:文章介 ...

  6. 用 SQL 写的俄罗斯方块游戏「GitHub 热点速览」

    在开始介绍上周热门开源项目之前,要插播一条开源新闻:Nginx 已正式迁移至 GitHub. 近日,Nginx 官方宣布将 Nginx 开源项目,从 Mercurial 迁移至 GitHub 代码托管 ...

  7. postgresql 查询包含某字段的表

    查询包含某字段的表都有哪些 查询SQL如下: SELECT b.oid, b.relname, att.attname, b.relkind, attinhcount, atttypmod FROM ...

  8. JS之AMD、CMD、CommonJS、ES6、UMD的使用笔记

    前言 如下图: AMD与CMD的主要区别: 1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行.不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同). ...

  9. Angular Material 18+ 高级教程 – Material Ripple

    介绍 Ripple (波纹) 是 Material Design 中一个标志性的特色. 点击 button 会溅起水波的感觉. 参考 Docs – Ripples When to use it? 一般 ...

  10. Blazor静态服务端呈现(静态SSR)身份认证

    本文介绍 Blazor 静态服务端呈现(静态 SSR)模式下,用户登录身份认证是如何实现的. 1. SSR 简介 SSR 是服务器侧呈现,HTML 是由服务器上的 ASP.NET Core 运行时生成 ...