C# 的 async/await 其实是stackless coroutine

注: 最近Java 19引入的虚拟线程火热,还有很多人羡慕 go的 coroutine,很多同学一直有一个疑问: C# 有 虚拟线程或者 coroutine吗,下面的这个回答可以解决问题。这里节选的是知乎上的hez2010 的高赞回答:https://www.zhihu.com/question/554133167/answer/2690808608 

C# 的 async/await 其实就是一个通用的异步编程模型,编译器会对 async 方法采用 CPS 变换,以 await 为分界线将方法进行拆分,然后使用一个状态机来驱动执行。用现在比较潮的说法就是 stackless coroutine。

例如说以下代码:

26acbc6ac25d96db32a9710ca7cf5bc8.png

编译完之后就变成类似这样的玩意(极度简化版)

009163a0ab57676b5ad093e41cde3635.png

其中的 Scheduler 是交由用户自己实现的,.NET 默认用线程池来做 Scheduler,但并不妨碍一些框架可以自行设计一个事件循环来做成 JavaScript 的 Promise。.NET 还提供了 AsyncMethodBuilder 的 type trait 来让你自己实现这个状态机和你自己的 Task 类型,因此你可以最大程度发挥想象来编写你想控制的一切。

你可以发现 async/await 本身并没有涉及到任何线程、调度相关的细节内容。换言之,async/await 是一个纯编译器特性。C# 在推出 async/await 的时候,考虑到方便用户的使用内置了 Task 和基于线程池的调度器满足一般使用,我们一般叫这个为 async runtime,其实就是根据上面所说的 type trait 实现出来的东西,后来为了减少分配又有了 ValueTask,以及 ASP .NET Core 为了性能又自己实现了个 PooledValueTask,Blazor WebAssembly 又因为浏览器平台不支持多线程于是实现了个基于事件循环的单线程 Scheduler,Unity 社区还有自己做的 UniTask 等等。当然,用户自己也可以实现自己的 async runtime。而 C++、Rust 则一开始只是将其作为语言特性推出,async runtime 则完全交给用户实现,标准库里除了一些 async primitives 之外什么都不带,于是 Rust 里出现了 async-std 和 tokio 等 async runtime,而 C++ 的话社区实现了一大堆的 async runtime,然后标准委员会还没吵出要怎么实现 STL 里的 async runtime。

啊,扯远了,继续说优缺点。

stackless coroutine 不需要寄存器上下文备份和恢复,需要引用什么局部变量只需要很简单的将它们提升到这个状态机的闭包里即可,而且是需要什么才提升什么,如果只有一个 int 需要引用,那就只需要一个装箱后的 int 那么多字节的堆内存,大概也就十几 B。此外,它遵循正常的分支判断和函数调用,因此不会打断 CPU 的控制流,分支预测和缓存友好。

而类似 goroutine 的 stackful coroutine 方案则是在用户态自己模拟系统线程来做了个用户态线程,然后在运行时上自己调度,于是每一个 goroutine 都需要创建自己的 stack 用来保存上下文(对应线程的 stack),这一个 stack 就是至少 8K,开多了占用会变得非常大。而且这种方案需要操作寄存器来进行上下文的备份和恢复,会打断正常的 CPU 控制流,使得分支预测失误和缓存缺失问题非常严重。唯一的好处就是不需要修改代码,对已有老代码改造起来非常方便。但是如果不存在已有老代码这种东西的话,这种方案可以说一点优势都没有,我始终认为 stackful coroutine 只是一个在已有老代码已经不方便修改了才应该使用的东西,否则完全没有意义。

综上所述,async/await 就是一个通用的异步编程方案,这个“异步”和它的调度方式是不绑定的,由用户想怎么实现就怎么实现,因此可以做到最高的灵活度;并且由于不需要维护所谓的虚拟线程的 stack,只需要将用到的局部变量提升到闭包内即可,资源占用也很低;并且由于不打断 CPU 控制流,性能更高,而且一些情况下是可以直接把 coroutine inline 掉的。

缺点自然也很明显,那就是代码不好编写,而且对代码有侵入性。如果要用 async/await,那必然需要将所有需要改造的阻塞调用改成 async/await,否则就约等于没有异步。

最后提一嘴性能问题,网上大为流传的 Go vs C#, part 1: Goroutines vs Async-Await | by Alex Yakunin | Medium,其中给 C# 测试代码加入了毫无意义的 await Task.YieldAsync(),带来了大量无意义的调度开销,这等价于给测试代码里面加 Thread.Sleep(1),何必呢。况且这个测试当时甚至用的是几乎一点优化都没有的 .NET Core 1.1,把原文用的 Go 和 .NET 升级到今天的 Go 1.19 和 .NET 7.0,C# 带着 await Task.YieldAsync() 这一行故意负优化都能跑到跟 Go 不相上下,去掉之后更是只需要不到 Go 一半的时间。

而且由于前面所说的 stackful coroutine 内存问题,没有大内存是没法流畅跑完 Go 的测试的,因为 goroutine 的内存开销非常大,在这个测试中需要耗费几个 G 的内存,如果内存不够会导致大量 GC 使得效率非常低下;对应 C# 的版本几乎没有什么内存消耗,十几 M 内存就够跑完了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中文词频统计

import jiebafoopen(text.txt,r,encodingutf-8)tfo.read()fo.close() wordsjieba.cut(t)dic{}for w in words: if len(w)1: continue else: dic[w]dic.get(w,0)1wc list(dic.items())wc.sort(keylambda x:x[1],reverse True)for i in range(20): print(wc[i]) 转载于:https:/…

[BZOJ1509][NOI2003]逃学的小孩

1509: [NOI2003]逃学的小孩 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 968 Solved: 489[Submit][Status][Discuss]Description Input 第一行是两个整数N(3  N  200000)和M,分别表示居住点总数和街道总数。以下M行,每行…

关闭 Visual Studio 2013 的 Browser Link 功能

什么是 Browser Link ? 这个 Browser Link 的功能就是通过一个脚本文件架起流程器和 Visual Studio IDE 之前的一个通信桥梁, 在启用 Browser Link 后, Visual Studio 会给网站注入一个 IHttpModule 模块对象, 然后在每个页面都会注册一段上…

Groove list操作-转数组,collect,each等

2019独角兽企业重金招聘Python工程师标准>>> list转换为数组 List list [a,b,c,d] def strs list as String[] println strs[0] 使用了Groovy语言,就能时不时的感受到Groovy语言在编码风格上与Java语言的不同。当然,我们首先感受到的可能就…

支持多种操作系统的新一代服务主机

一个应用需要常驻操作系统后台服务,可选框架有WindowsServiceLifeTime和SystemdLifeTime,但需要区别对待不同操作系统且需要另外写命令安装。NewLife.Agent自2008年设计以来,一直秉着简单易用的原则,不仅实现了服务框架&#xff0…

[翻译]Dapr 长程测试和混沌测试

介绍这是Dapr的特色项目,具体参见:https://github.com/dapr/test-infra/issues/11 ,在全天候运行的应用程序中保持Dapr可靠性至关重要。在部署真正的应用程序之前,可以通过在受控的混沌环境中构建,部署和操作此类应用程…

Mysql Lost connection to MySQL server at ‘reading initial communication packet', system error: 0

一、问题描述: 在服务器端可以正常连接并操作mysql,但是在windows端使用navicat工具远程ssh连接就出现下面错误。 1、服务器端: 2、windows端navicat连接 3、原因 原来我今天在做主从配置的时候,将 /etc/my.cnf 配置文件中的b…

自定义ProgressBar(圆)

2019独角兽企业重金招聘Python工程师标准>>> <lib.view.progressbar.ColorArcProgressBarandroid:layout_width"match_parent"android:layout_height"220dip"android:id"id/barInterest"android:layout_centerInParent"true&…

C# Task用法详解

概述Task是微软在.Net 4.0时代推出来的&#xff0c;Task看起来像一个Thread&#xff0c;实际上&#xff0c;它是在ThreadPool的基础上进行的封装&#xff0c;Task的控制和扩展性很强&#xff0c;在线程的延续、阻塞、取消、超时等方面远胜于Thread和ThreadPool&#xff0c;所以…

函数调用堆栈图

转载于:https://www.cnblogs.com/DeeLMind/p/7617972.html

Session的原理,大型网站中Session方面应注意什么?

一、Session和Cookie的区别 Session是在服务器端保持会话数据的一种方法&#xff08;通常用于pc端网站保持登录状态&#xff0c;手机端通常会使用token方式实现&#xff09;&#xff0c;存储在服务端。 Cookie是在客户端保持用户数据&#xff0c;存储位置是客户端&#xff08…

两圆相交求面积 hdu5120

转载 两圆相交分如下集中情况&#xff1a;相离、相切、相交、包含。 设两圆圆心分别是O1和O2&#xff0c;半径分别是r1和r2&#xff0c;设d为两圆心距离。又因为两圆有大有小&#xff0c;我们设较小的圆是O1。 相离相切的面积为零&#xff0c;代码如下&#xff1a; [cpp] view …

Python_list部分功能介绍

x.append():在列表尾部添加一个元素 x.clear():把列表清空 x.count():判断某个元素出现的次数 x.extend():合并两个列表&#xff0c;或者一个元组 x.index():获取元素下标 x.insert():指定下标添加元素 x.pop():移除某一元素&#xff0c;移除的元素可获取 x.remove():移除指定的…

一招解决开发环境问题 —— 远程容器开发指南

前言使用C作为主要开发语言的程序猿们应该会认同搭建开发环境是一件烦人的事情。为了编译一个程序不仅需要下载各种依赖包&#xff0c;还可能面临本地系统不兼容、编译器版本不一致、包版本冲突等各种问题。笔者在运营iLogtail开源社区的过程中发现开发和调试环境问题也是成员问…

UITabBarController的基本原理及使用(一)

前言 UITabBarController在iOS开发中是一个高频使用的控制器&#xff0c;典型的案例如QQ、微信均使用UITabBarController布局。本文将从一个新建工程&#xff0c;和大家一起了解UITabBarController的基本原理和使用方法。 基本概念 UITabBarController能够方便地管理多个控制器…

C# 多线程ThreadPool用法举例

概述ThreadPool是.Net Framework 2.0版本中出现的。自从Task出来以后&#xff0c;ThreadPool已经很少用了&#xff0c;但是一些老的代码或者一些古老的程序猿还是会用到他&#xff0c;所以我们可以不用它&#xff0c;但是还是有必须学习和了解他.ThreadPool用法举例static void…

Mysql实现主从复制(一主双从)

一、环境介绍 LNMP&#xff08;centos7&#xff0c;mysql5.6&#xff09; vmware workstation pro配置了3个虚拟机&#xff0c;均安装了LNMP环境&#xff1a; master&#xff1a; 192.168.0.105 slave&#xff1a; 192.168.0.106 、192.168.0.107 二、原理 &a…

接口文档神器Swagger(下篇)

本文来自网易云社区作者&#xff1a;李哲二、Swagger-springmvc原理解析上面介绍了如何将springmvc和springboot与swagger结合&#xff0c;通过简单配置生成接口文档&#xff0c;以及介绍了swagger提供的一些注解。下面将介绍swagger是如何做到与springmvc结合&#xff0c;自动…

利用python进行数据分析D1——ch02引言

基础的课程还没学完&#xff0c;就来这本了&#xff0c;因为我平时的研究还是以数据的处理为主。把自己的事做好做细致读了一下介绍部分&#xff0c;下载书里用到的数据&#xff0c;下载地址&#xff1a;https://github.com/wesm/pydata-book 如果你需要完成以下几大类任务&…

记一次Memory Leak分析

起因&#xff1a;最近公司的一个web产品遇到了内存溢出&#xff0c;于是开始着手调查。调查&#xff1a;首先当务之急是找到那个或那些API导致Memory Leak&#xff0c;这个应该不难&#xff0c;根据监控分析&#xff0c;在内存上升时间段内有哪些API被访问&#xff0c;再就是根…