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.pswp.cn/news/282275.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282275.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282275.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

推薦使用typora

最近在網上接觸到一款全新的markdown寫作工具——typora。 現在它已經是我的主要寫作工具了。 甚至我也也會利用它安排自己的工作和任務。 typora介紹 下載鏈接特色:可以即時渲染markdown語法的書寫工具總算找到了,終于不用再糾結發生語法錯誤&#xff0…

中文詞頻統計

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:/…

實現html錨點的兩種方式

1,a標簽name屬性。 2,使用標簽的id屬性;

mysql實現讀寫分離

一、環境介紹: LNMP vmware workstation pro配置了3個虛擬機,均安裝了LNMP環境: Pro :192.168.0.105 Pro2:192.168.0.106 Pro3:192.168.0.107 二、Mysql主從復制同步的實現 https://blo…

[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行,每行…

十一隨筆|讀書

十一放假回老家前三天一直下雨,沒法幫父母干農活,陰雨天氣農村就閑下來了親戚間走動,長輩們談論孩子不好好學習,孩子抱怨學習沒用大學畢業照樣找不到工作。現在大學生就業現狀確實不容樂觀,當下不好好學習沒有拖底&…

yii之behaviors

BaseController: protected $actions [*];protected $except [];protected $mustlogin [];protected $verbs [];// 行為過濾public function behaviors(){return [access > [class > \yii\filters\AccessControl::className(),only > $this->actions, // 針對哪…

關閉 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…

c#中的奇異遞歸模式

奇異遞歸模式,Curiously Recurring Template Pattern (CRTP) ,作用是能使父類中能夠使用子類的信息。下面是我對這個問題的分析過程。 按照一般的繼承關系,父類是無法訪問到子類的,所以很自然的想到了c#中的泛型,將子類…

面試中get和post的區別

get和post的區別主要有以下幾方面:1、url可見性: get,參數url可見; post,url參數不可見2、數據傳輸上: get,通過拼接url進行傳遞參數; post,通過body體傳輸參數3、緩存性…

程序猿與線性代數

逛微博,摸到了一堆寶:關于線性代數學習的文章。先是發現了陳曉鳴(http://weibo.com/acumon),前百度資深project師,終身學習者。再找到“文藝復興記”(http://weibo.com/weidagang)。…

Verilog MIPS32 CPU(八)-- 控制器

Verilog MIPS32 CPU(一)-- PC寄存器Verilog MIPS32 CPU(二)-- RegfilesVerilog MIPS32 CPU(三)-- ALUVerilog MIPS32 CPU(四)-- RAMVerilog MIPS32 CPU(五)--…

[翻譯]Dapr 長程測試和混沌測試

介紹這是Dapr的特色項目,具體參見:https://github.com/dapr/test-infra/issues/11 ,在全天候運行的應用程序中保持Dapr可靠性至關重要。在部署真正的應用程序之前,可以通過在受控的混沌環境中構建,部署和操作此類應用程…

python UDP-數據報協議

基于udp協議通信的套接字 服務端 1 from socket import *2 3 server socket(AF_INET, SOCK_DGRAM) # SOCK_DGRAM>數據報協議4 server.bind((127.0.0.1, 8080))5 6 print(start....)7 while True:8 data, client_addr server.recvfrom(1024) # (bhello, (127.0.0.1, …

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