当前位置: 首页 > news >正文

NET 中 Async/Await 的演进:从状态机到运行时优化的 Continuation

NET 中 Async/Await 的演进:从状态机到运行时优化的 Continuation

 

C# 的 `async/await` 长期以来是编写简洁、非阻塞代码的基石,但其传统实现——每个异步方法生成一个独立状态机——在高性能场景(如递归或链式异步调用)中暴露出显著局限性。2025 年的 .NET 9 和 .NET 10 引入了革命性方案:运行时动态管理的异步处理(runtime-handled async)。本文基于近期技术讨论,精炼梳理传统模型的缺陷、新方案的核心,以及其对性能和开发的深远影响。

## 传统 Async/Await 的缺陷

C# 5.0(2012)引入的 `async/await` 让异步代码如同步般直观,但其编译器驱动的设计存在明显问题:

- **每个方法一个状态机**:Roslyn 编译器将每个 `async` 方法转为状态机结构体(`IAsyncStateMachine`),用 `switch` 跟踪 await 点。例如,递归方法 `async Task<long> FibAsync(int n)` 有两个 await(`n-1` 和 `n-2`),每次调用生成新状态机和 `Task` 对象。若 `n=30`,指数级调用(约 2^n)导致数百万分配,GC 压力巨大。
- **内存与 GC 开销**:每个状态机和 `Task`(约 100-200 字节)都在堆上分配,状态机装箱(boxing)进一步增加成本。长链或递归场景下,内存使用和 GC 暂停使性能下降 20-50%(如高吞吐 Web API)。
- **协调与调试复杂**:多状态机通过 `Task` 的 Continuation 协调,链长或递归时回调嵌套导致延迟(增加 10-20%)和调试困难(栈碎片化)。
- **动态场景瓶颈**:如 `FibAsync`,递归展开的 await 点无法在编译时预测,静态生成的状态机无法优化,分配爆炸。

尽管功能上“能跑通”,但性能缺陷明显,尤其在高性能场景下。

## 新方案:运行时动态管理异步

.NET 9 和 .NET 10(2025 年 RC1)通过 runtime-handled async(或 AsyncHelpers)彻底革新了异步模型,解决了传统缺陷:

- **编译时仅标注**:编译器不再为每个 `async` 方法生成完整状态机,仅标记 await 点、局部变量和 Continuation 位置,生成轻量 IL 代码。这简化了编译输出,移交动态逻辑给运行时。
- **运行时动态管理**:JIT 或 CLR 在运行时根据标注动态构建 Continuation 链,隐式管理状态(类似单一状态机效果)。这通过 JIT 优化(如内联、PGO)或 VM 层实现,减少状态机实例和 `Task` 分配(从 O(n) 到 O(1) 或 0)。
- **ValueTask 集成**:运行时广泛使用 `ValueTask`(值类型,栈优先)作为 Continuation 载体,同步场景零分配,异步时共享轻量源(如 `IValueTaskSource`),大幅减少 GC 压力。
- **线性 Continuation 链**:运行时将异步链抽象为“线性节点序列”(类似 continue 标签),每个 await 点像入栈/出栈操作,等待和恢复顺序与代码呈现一致,简化协调并避免传统嵌套回调的复杂性。

以 `FibAsync` 为例:
```csharp
async ValueTask<long> FibAsync(int n) {
if (n <= 1) return n;
return await FibAsync(n - 1) + await FibAsync(n - 2);
}
```
传统模型为每次递归生成状态机和 `Task`,指数级分配;新方案中,运行时动态融合 Continuation,减少 30-50% 内存,延迟降低 10-20%。

## 异步调用链的线性与并发

异步调用链(如 `await MethodA() -> await MethodB()`)在时间和逻辑上是线性的,await 点的顺序与代码呈现一致,运行时通过动态 Continuation 链强化这一特性,确保直观性和可预测性。 同时,.NET 支持并发:
- **并发能力**:通过 `Task.WhenAll` 或 `Task.Run`,多个异步链可并行运行,利用多核处理器。例如:
```csharp
async Task Run() => await Task.WhenAll(Chain1(), Chain2(), Chain3());
```
每个链内线性,链间并行,运行时优化调度(.NET 10 改进 ThreadPool 工作窃取),提升吞吐量 ~15%。
- **根节点识别**:每个 `async` 方法是链的根节点,运行时从根方法(如 `Chain1()`)开始动态解析 Continuation,管理整个链。开发者只需指明根,运行时处理复杂逻辑。

## 其他优化细节

新模型的性能提升不仅来自运行时管理,还包括:
- **移除 Boxing**:.NET 9 优化 `AwaitUnsafeOnCompleted`,减少状态机装箱,降低 GC 开销。
- **JIT 与 PGO**:动态优化 Continuation 链,内联和循环展开提升递归场景性能(如 `FibAsync`)。
- **GC 改进**:.NET 10 的动态阈值 GC 缓解高分配场景的暂停问题。

## 总结与展望

从编译时状态机到运行时动态管理的转变,.NET 9/10 解决了传统 `async/await` 的核心缺陷:高内存分配、协调复杂和动态场景瓶颈。异步调用链的线性本质和并发支持(通过 `Task.WhenAll`)在运行时优化下更加高效,`ValueTask` 和 JIT 技术进一步减少开销。未来,.NET 10 或更高版本可能完全取代传统模型,提供更无缝的异步体验。

**实践建议**:
- 使用 `async ValueTask` 重写高频或递归方法,测试性能提升。
- 用 BenchmarkDotNet 对比 .NET 8 和 10 的异步性能。
- 关注 GitHub dotnet/runtime(如 #94620)获取最新 AsyncHelpers 进展。

.NET 的异步编程正迈向更高效、直观的未来!

相关文章:

NET 中 Async/Await 的演进:从状态机到运行时优化的 Continuation

NET 中 Async/Await 的演进:从状态机到运行时优化的 Continuation C# 的 `async/await` 长期以来是编写简洁、非阻塞代码的基石,但其传统实现——每个异步方法生成一个独立状态机——在高性能场景(如递归或链式异步调用)中暴露出显著局限性。2025 年的 .NET 9 和 .NET 10 …...

最长公共子序列

题目描述 给你一个序列X和另一个序列Z,当Z中的所有元素都在X中存在,并且在X中的下标顺序是严格递增的,那么就把Z叫做X的子序列。 例如:Z=是序列X=的一个子序列,Z中的元素在X中的下标序列为<1,2,4,6>。 现给你两个序列X和Y,请问它们的最长公共子序列的长度是多少? …...

使用 Ansible 管理服务器集群

Inventory Ansible 使用 /etc/ansible/hosts 管理受控服务器列表: --- ungrouped:hosts:node-1:ansible_host: 192.168.1.1ansible_user: johnnode-2:ansible_host: 192.168.1.2ansible_user: janenode-3:ansible_host: 192.168.1.3ansible_user: frank关于 inventory 文件的字…...

1现在处于非常破防的阶段,不知道为什么会打成这个样子。 ABC 过得很快。看到 D1 的第一眼就会了,发现转移只需要随便优化一下就能通过 D2,不太想写。E 看上去挺可做,F 看上去是板子题。于是开始写 F,不知道这种代码不长、没有任何思维难度的题怎么能写那么长时间,根本原因…...

Codeforces Round 1051 (Div. 2)

A. All Lengths Subtraction 题意:一个排列,对于每个\(k \in [1, n]\),你都要选择一个长度为\(k\)的子数组使得它们都减一,求有没有方案使得最终所有数都是\(0\)。 考虑\(k\)从大到小,发现做\(n\)的时候\(1\)变成\(0\),此时如果\(1\)不在两端,则\(k = n - 1\)时必然再把…...

再不学就晚了!RDT LeRobot与RDKS100部署详解

作者:SkyXZ CSDN:SkyXZ~-CSDN博客 博客园:SkyXZ - 博客园 机械臂:LeRobot-SO101 数采机:MacBook-Pro Python3.10 开发机:Ubuntu 22.04, Cuda12.4,8 NVIDIA A100-SXM4-40GB 开发板:RDK OS 4.0.2 Based on Ubuntu 22.04, Python 3.10.12, OpenExplore 3.2.0 相…...

编译Unity4.3.1f1

参考: 编译 Unity 4.3.1 引擎_unity-source-4.3.1f1-CSDN博客 Unity 4.3.1f1编译调试 - 知乎 Unity source 4.3.1f1 源代码分析-腾讯游戏学堂 附: 早期版本下载(4.x之前的版本)...

【R课堂-电机专栏】为什么提高电机的电压时,转速会随之上升?

本文探讨的问题是 “为什么提高电机的电压时,转速会随之上升?”具体而言,就是当给电机绕组施加的电压升高(增大)时,为什么其转速会随之上升。这一现象看似理所当然,但其背后的原理却涉及诸多物理公式。这个问题对于深入了解电机原理非常关键,下面将为大家详细阐述。●问…...

抽象 CF

一道题在 CF 上有三倍经验,我有个细节假了: \(n \le 10^5\),84 个点的那道在 #64 寄了。 \(n \le 2 \times 10^5\),88 个点的那道在 #88 寄了。 \(n \le 5 \times 10^5\),111 个点的那道直接 A 了。...

单元测试之Mockito使用

测试中为什么需要Mock 在实际的测试中,被测试的对象并不都是可以通过简单的new操作符就可以创建出来的! 实际的业务代码中,一个业务类往往要依赖很多其他的类才能完成一个完整的业务方法,这些依赖包括第三方的rpc,db的查询等等,具体的拓扑如下图我们想测试ClassA,但是Cl…...

Jetson有Jtop,Linux有Htop,RDK也有Dtop!

作者:SkyXZ CSDN:SkyXZ~-CSDN博客 博客园:SkyXZ - 博客园 本项目基于btop开源项目进行二次开发,旨在为RDK平台提供更强大的系统监控工具。 Linux系统下有Htop可以作为系统监控,英伟达的Jetson也有第三方的Jtop,咱们RDK虽然也提供了hrut_somstatus来查看BPU的使用…...

《原子习惯》-读书笔记4

2025.09.17 Day4 1、 养成习惯的过程可以分为四个简单的步骤:提示、渴求、反应和奖励。2、你的头脑在不断分析你的内外部环境,寻找奖励所在的线索。因为线索是我们已然接近奖励的第一个迹象,它自然会导致人们滋生渴求。3、提示的作用是让你注意到奖励的存在。渴求是想要得到…...

Java学习第二天

数据类型 Java、c++是一种强类型语言要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用,安全性高,速度较慢 弱类型语言:安全性低,速度较快 Java数据类型分为两大类:基本类型、引用类型位:计算机内部数据存储的最小单位,11001100是一个八位二进制数 字节:是…...

Java学习第三天

顺序结构 package Scanner;import java.util.Scanner;public class Demo05 {public static void main(String[] args) {//我们可以输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并非输出执行结果Scanner scanner=new Scanner(System.…...

Java学习第四天

break continue break在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句(break语句也在switch语句中使用) continue语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行…...

搜索百科(1):Lucene —— 打开现代搜索世界的第一扇门

大家好,我是 INFINI Labs 的石阳。 这是《搜索百科》专栏系列文章,每天 5 分钟,带你速览一款搜索相关的技术或产品,同时还会带你探索它们背后的技术原理、发展故事及上手体验等。 搜索技术看似专业,但它早已深度融入我们的日常生活。无论是电商搜索、知识检索,还是 AI 语…...

02020308 .NET Core核心基础组件08-结构化日志和集中日志服务

02020308 .NET Core核心基础组件08-结构化日志和集中日志服务...

zookeeper的配置

问题:1.1号和二号虚拟机可以成功启动三号报错 2025-09-17 17:57:46,219 [myid:] - INFO [main:QuorumPeerConfig@133] - Reading configuration from: /export/server/apache-zookeeper-3.5.6-bin/bin/../conf/zoo.cfg 2025-09-17 17:57:46,228 [myid:] - INFO [main:Quorum…...

02020307 .NET Core核心基础组件07-什么是Logging、NLog

02020307 .NET Core核心基础组件07-什么是Logging、NLog...

算法第一周博客

算法第一周博客任务一,搜索大公司内部编码规范,列出本学期编码需遵守的规范 1.程序块要采用缩进风格编写,缩进的空格数为4个,使得层次分明。 2.控制每行最大长度不超120个字符,超过时换行并适当缩进,并且一行通常一个语句。 3.命名标识符(包括变量,函数,结构体等)时,…...

nid修改dbid/dbname

Table of Contents1. 简述 2. 操作步骤说明 3. 操作命令1. 简述2. 操作步骤说明如果配置了DBconsole,需要删除DBconsole对象。之后完全关闭,再启动到mount状态。 nid 命令支持修改dbid 和 dbname两个值。具体操作见之后示例。 但是在操作之前,需要明确是只需要修改dbid 还是同…...

攻防世界-parallel-comparator-200 - xxx

下载后得到是一个c语言文件,用vs创建一个项目后复制代码发现无法打开<pthread.h>,去网上搜了一下发现windows平台配置有点麻烦 于是就去搜索了一下这个的作用,后面分析代码的时候再提。 先看main函数user_string就是用户需要输入一个长度为20的字符串,然后去看能够触…...

Manim实现脉冲闪烁特效

在数学可视化中,脉冲闪烁特效能像聚光灯一样引导观众注意力,突出关键公式、特殊点或重要结论。 本文将介绍如何一步步通过代码来实现这个特效,并通过参数精准控制视觉效果。 1. 实现原理 脉冲闪烁特效的核心是周期性改变发光体的半径和透明度,模拟能量波动的视觉效果。 这个…...

2025.9.17总结

今天主要内容就是在IDEA里编写代码,后端还是springboot,前端vue3,数据库用hbase。 其他的和之前编写的web项目流程都一样。 就是数据库的使用,和数据库的读写,操作不太一样。 还有hbase,创建表,表结构,读写和常规的mysql不一样。在ai帮助下完成创建表,表结构,读写数据…...

office2024安装包下载安装教程(2025最新整理)office2024专业增强版下载安装教程

在当今数字化办公的时代,一款功能强大且稳定的办公软件是提高工作效率的关键。Office 2024 专业增强版作为微软办公软件家族中的佼佼者,集成了多种实用的办公组件,能够满足各类用户在文档处理、数据管理、演示汇报等多方面的需求。本文将为大家详细介绍 Office 2024 专业增强…...

2025竞赛学习资料

2025竞赛学习资料链接1、竞赛资源链接集 2、CSP-J/S资源链接1 3、C++竞赛综合 4、2025年3月GESP认证C++5级判断题真题详解 5、GESP考试集...

C++ 模板参数推导问题小记(模板类的模板构造函数)

本篇主要是为了记录在编写一个模板类的模板构造函数中遇到的初始化问题,以及针对这个问题展开的相关知识整理,文章就以引发这个问题的代码为标题了。 问题代码 在编写一个代表空间点的模板类 point 时,我打算为它添加一个模板构造函数:代码template<typename T, std::si…...

axios两种写法

// 登录 export const login = (data) => {return request.post(/login, data) }// 权限管理列表 get请求需要写在url上面 export const authAdmin = (params) => {return request.get(/auth/admin, { params }) } ///////////////////////////////////////////////////…...

adobe illustrator中使用画笔工具切割图形

001、测试图形 002、选择画笔工具 3、绘制想要切合的形状,然后同时选中这跟线和图形 004、窗口 + 路径查找器 005、 点击分割 006、 点击取消编组 007、 实现图形分割 。...

2025年了,在 Django 之外,Python Web 框架还能怎么选?

前言 大家好,我是曦远~ 前段时间我写过一篇文章 《Django过时了吗?从ASGI到AI时代的思考》,聊到在 AI 时代下,传统全家桶式框架(比如 Django)该如何面对新趋势。 最近在翻新 DjangoStarter 项目的时候,我顺便做了一些调研,发现 Python Web 生态的变化比我想象得还要快。…...

AtCoder Beginner Contest 423

D - Long Waiting 三个优先队列 #include<bits/stdc++.h> using namespace std; #define endl \n #define yes cout << "YES" << endl #define no cout << "NO" << endl #define pii pair<int,int> #define ll long l…...

SRAM和DRAM的特点和区别

SRAM 静态随机存取存储器 基本结构和工作原理:核心单元:SRAM每个cell 由6 个晶体管 组成,形成一个双稳态触发器电路。 工作原理:这种电路结构由两个稳定的状态,分别代表逻辑“0”和逻辑“1”。只要保持通电,状态就会一直保持下去,不需要额外的操作。 读写过程:通过字线…...

xml基本语法

1. xml的基本结构 XML(可扩展标记语言,eXtensible Markup Language)是一种用于存储和传输结构化数据的标记语言,核心特点是自定义标签和严格的语法规则.一个合法的 XML 文档必须包含文档声明和唯一根元素<?xml version="1.0" encoding="UTF-8"?&g…...

Java25新特性

🛠️ 1. 语言特性与开发者体验实例主方法 (Instance Main Methods)​: 支持省略 public static修饰符的 void main()方法,使初学者更易编写第一个Java程序。// 无需显式类声明和public static修饰符 void main() {IO.println("Hello, JDK 25!"); // java.lang.IO …...

Day17多维数组

多维数组可以看成数组的数组,即在数组中在嵌套一个数组 例如二维数组是一个特殊的一维数组,他的每一个元素都是一个一维数组(1,2,3,4,5.......) 格式与一维数组相似:int [] [] a = new int [i] [j]; 举例的二维数组可以看为两行五列的数组 public class ArrayDemo5 {pub…...

C++ lambda 匿名函数

1、基本介绍 C++11 引入的 lambda 匿名函数(Lambda Expression)是一种轻量级的函数对象,可在需要函数的地方直接定义,无需单独声明,极大简化了代码编写(尤其是回调函数、算法谓词等场景)。 基本语法: [capture-list] (parameter-list) mutable noexcept(optional) ->…...

D拼数

include<stdio.h> include<stdlib.h> #include<string.h> int compare(const void *a, const void *b) { char str1[40], str2[40]; // 1:a在前,b在后 sprintf(str1, "%d%d", *(int *)a, *(int *)b); // 2:b前a后 sprintf(str2, "%d%d&q…...

20250917 - WETToken 攻击事件:价格操控产生的套利空间

背景信息Alert:https://x.com/TenArmorAlert/status/1968223320693686423 TX:https://app.blocksec.com/explorer/tx/bsc/0xf92539acf7eadfd4a98925927a52af5349cb13c2a250908373a5baf8ea4b49adTrace 分析 发生攻击的位置在闪电贷的 callback 函数里面,执行完闪电贷后攻击合…...

题解:P6798 「StOI-2」简单的树

简单的树: 题意: 一颗树,每个节点有一个权值 \(c_i\)。 \(val_i\):\(i\) 为根的子树内所有 \(c_i\) 的最大值。 \(f(x,y)\):\(c_{x}\) 改为 \(y\) 后 \(val_i\) 之和。 每次询问给定 \((l,r,a)\) ,求 \(\sum\limits_{i=l}^{r}{f(a,i)}\)。 思路 首先一眼看出来几个性质:…...

题解:P11704 [ROIR 2025] 旅行路线

旅行路线: 很有参考价值的一道题,其他题解有点抽象,我来。 转化题意 题意转化为 \((1,2)→(n-1,m),(2,1)→(n,m-1)\) 的两条链不相交且经过所有关键点的方案数。 其他点没用,我们以下的点指关键点。 无不能相交限制的 DP 由于 \(x_i\le x_j,y_i\le y_j\),\(i\) 才可以转移…...

题解:P11292 【MX-S6-T4】「KDOI-11」彩灯晚会

彩灯晚会:\(n\) 点 \(m\) 边 \(k\) 种颜色,给每个点染色。 \(cnt_i\):第 \(i\) 种颜色长度为 \(l\) 的链的数量。其中 \(l\) 为题目给的一个常量。 求 \(\sum_{染色方案}\sum_{i=1}^k cnt_i^2\) 的和。一\(\sum_{染色方案}cnt_i\) 值都一样,钦定 \(pos\) 作为代表颜色,那么…...

算法课程第一周作业

《数学之美》第一章启示 《数学之美》的第一章,在算法工程师眼中,并非传授某个具体算法.而是重构了我们理解、设计和应用算法的底层思维框架,世界的基本问题是算法问题,而数学是寻找最优算法的终极语言。 启示一:所有问题本质上都是建模与算法选择问题.意味着世界是一个巨大的待…...

实测对比:权威榜单之微信排版Top 5编辑器大揭秘

在新媒体运营的世界里,微信排版可是重中之重,它直接影响着文章的视觉效果和读者的阅读体验。很多运营人都有这样的痛点:写作慢、排版耗时、跨平台排版不统一、配图难还可能有侵权风险等。为了帮大家解决这些难题,我亲测了有一云AI编辑器、智撰AI编辑器等多款主流编辑器。在…...

自建仓库推送到NAS采用 Docker Registry 工作流

放弃手动 `save` 和 `load` 的方式,改用行业标准的 Registry(仓库)模式。这是最专业、最高效的方案。 **优点**: - **彻底解决版本兼容性问题**,因为 push/pull 协议是标准化的。 - 传输效率高,再次推送时只会上传有变动的层(layer)。 - 是 DevOps 和自动化流程的基础,…...

【汇编和指令集 . 第2025 . 9期】发现大牛

【编者按】在计算机、互联网风行半个世纪之后,我们发现:科技预言家越来越多了,思想家缺位了。生活节奏变快了,思想退步了;书写减少了,纸张缺没少;知识泛滥了,思考没有深入......我们有可能遭AI时代的反噬。时代呼唤跨文理的大家,呼唤有温度的电子产品。发刊词: …...

Opencompass避坑日记

安装首先执行pip安装 再下载源代码第一句是为了安装opencompass的依赖包,第二句是为了在当前目录引入本地目录的opencomass模块。 因为有很多修改的地方。 测评VLLM 放弃吧,这个框架对VLLM的支持很差。测评方式:稳定的有且只有这一种python run.py \--datasets demo_gsm8k_c…...

随笔 | 农场、小猴子、香蕉

在一个偏西部的农场中,有着一群猴子,他们每天的任务,是将香蕉树上的香蕉摘下来,而他们的报酬是仅仅九根香蕉,每天早上四根,每天晚上五根。某一天,其中一只猴子报怨,每天早上只能吃到四根香蕉,他提议说,改成每天早上五根香蕉,其他猴子都纷纷表示同意,仅有一只小猴子…...

Day17数组的使用

package com.cc.array;public class ArrayDemo4 {public static void main(String[] args) {int [] arrays = {1,2,3,4,5};//jdk1.5之后的版本可以通果增强for寻循环来遍历数组或集合中的每一个元素//缺点在于没有下标//for(int array:arrays){// System.out.println(array);p…...

完整教程:缓存与数据库一致性的4大坑及终极解决方案

完整教程:缓存与数据库一致性的4大坑及终极解决方案pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monosp…...

Rust的Cargo用法详解 - 详解

Rust的Cargo用法详解 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-si…...