再談協程

来源:https://www.cnblogs.com/newton/archive/2019/07/08/11104187.html
-Advertisement-
Play Games

如果你對以下幾個問題有疑問,那麼本文可能會有所幫助。 1.2.3 談協程繞不開線程,按傳統還得從進程談起,不過我想業內人員對進程和線程應該是耳熟能詳,這裡就簡單概括下。 進程擁有自己獨立的堆和棧,既不共用堆,亦不共用棧,進程由操作系統調度;線程擁有自己獨立的棧,共用堆(也可以有自己的私有域),不共用 ...


如果你對以下幾個問題有疑問,那麼本文可能會有所幫助。

  1. 什麼是協程,或者說為什麼會有協程這個概念?
  2. 怎麼用?什麼時候需要用?
  3. 都有並行的意味,那麼協程和多線程有什麼區別?兩者能否相互替代?
  4. 協程底層的實現原理。

1.2.3

談協程繞不開線程,按傳統還得從進程談起,不過我想業內人員對進程和線程應該是耳熟能詳,這裡就簡單概括下。

進程擁有自己獨立的堆和棧,既不共用堆,亦不共用棧,進程由操作系統調度;線程擁有自己獨立的棧,共用堆(也可以有自己的私有域),不共用棧,線程亦由操作系統調度。一個進程可以有多個線程。

多線程一直以來是面試必考點,雖然[web]服務端開發人員似乎從來不用直接操作線程,其實是因為框架幫忙維護了,開發人員只需要關心業務實現。這也導致了部分人對多線程的某些概念模糊不清。比如關於多線程的效率:在多核cpu下,多個線程可以並行運行在不同內核上,效率高;而在單核cpu中,多個線程的並行執行其實是一個錯覺,因為它們都是運行在一個內核上,一個cpu內核同一時間只能執行一個進程/線程,因此在一個內核上的多線程執行其實效率反而比串列執行低,只是給用戶一種併發的錯覺,反而增加了線程切換的時間。

但是效率的高低還要看線程占用cpu資源的占用率,比如存在大量IO操作,IO比較慢。也就是說,如果只有單線程,那麼一旦涉及到IO操作,線程可能會被阻塞,程式的其餘邏輯就只能傻等,就算那些邏輯不依賴於這個IO操作,此時線程對CPU的使用為0,CPU就是空閑狀態。如果是多線程,是線程瓶頸,那麼其餘線程則可以使用cpu,而非等待IO結束。

題外話,一個空迴圈就能讓cpu滿載,參看 為什麼一個空的死迴圈會讓CPU占用達到100%

後來,出現了多路復用之類的技術,原先需要等待IO返回的線程也不需要等了,可以和其它線程一樣忙別的事,IO返回時得到通知再處理接下去的事情。Java的NIO和.Net的async/await就是這麼乾的。

一般來說,為了避免線程頻繁創建銷毀帶來的性能問題,程式里都會使用到線程池。

然而還是在單核的場景下,事情似乎變得有點詭異。既然線程們現在都能心無旁騖地使用CPU計算,而前面也說了,一個cpu內核同時只能運行一個線程,管理多線程又是搶占式,又是棧切換,維護生命周期啥的,影響性能不說,完全沒得必要嘛,為什麼不只用一個線程完成所有的計算呢。什麼,你說可能需要[偽]並行計算?那就讓線程自己來安排咯,畢竟具體邏輯方面,線程本身(或者說開發人員)比CPU要清楚的多,知道什麼時候該乾什麼,什麼時候切換邏輯,什麼時候不切換,都由線程自己說了算。於是,協程粉墨登場。

協程主要是針對單線程的一個概念(如Js、NodeJs、Python由於GIL導致的偽多線程),可以將其看作線程運行時片段。和線程類似,雖然貌似多個協程可以並行執行,一個時間仍然只能運行一個。所以,如果業務邏輯是順序相關(串列)或者各任務對反饋及時性要求不高,那麼沒必要用協程,就跟沒必要多線程一樣。協程對比線程,除了有更好的性能外,還讓開發人員對執行片段有了更好的掌控。比如Go語言,通過阻塞條件(time.sleep()、select{}等),我們可以手動將控制權轉移給其它的 Go 協程 , 也可以說是告訴調度器讓它去調度其它可用空閑的 Go 協程(Go如何判斷這是阻塞代碼尚未研究過);或者通過channel調度指定協程。

Go預設情況下只用單線程。這就是說,你即使開了幾百個goroutine,系統中同一時間在跑的只有一個線程,也就是一個協程。依據上面的內容,大家可以思考下Go為何預設如此。我們可以通過 runtime.GOMAXPROCS() 設置的是Go語言能跑幾個線程,講道理,CPU幾核跑幾個線程比較合理,使用 runtime.NumCPU() 查看內核數。

在編程層面來說,協程的概念偏向於以同步編程的模式實現非同步處理的編程模式,避免了多層回調代碼嵌套的問題。

其實在很多年以前,協程已經被提出了,現在只是它煥發生機的階段。


4

上文說了,協程之間應該是非順序相關的,即它們的上下文沒有強依賴關係,是相對獨立的。這裡的上下文指的就是當前的運行棧空間,它包括了參數、局部變數、各寄存器的值等內容。在協程切換的時候,我們要想辦法將對應的上下文投射到當前線程的運行棧中,即讓線程執行特定的上下文。很容易想到malloc一塊臨時記憶體存放掛起的協程上下文信息,resume的時候再覆蓋回去,運行棧在記憶體中只有一處,這就是stackless模式。相對的還有stackful模式,在這種模式下,每個協程都有自己的棧空間,運行棧指的就是當前協程的棧空間。現有語言的實現中,Python, Kotlin等定義的就是stackless協程, Go語言中實現的是stackful協程。

對於其它沒有在語言層面直接支持協程的語言來說,由於協程涉及到底層的[堆]棧切換控制,因此很難單純依靠現有語法構建演算法的方式實現。有人做過此類嘗試(可參看Coroutines in C),但也沒有實用性。

能直接操作執行堆棧並暴露api的,現在市面上的語言以C/C++最為流行,基於它們也有很多開源的協程庫。下麵介紹幾種實現方式。

協程分為非對稱協程和對稱協程。在非對稱協程中,調用者和被調用者的關係是固定的,調用者將控制流轉到被調用者,被調用者運行完畢後只能返回到調用者,而不能返回到其他協程。對稱協程則不然。對稱協程可以很容易由非對稱協程來表達。且按一般的調用邏輯,A調B,B應返回到A,再由A發起到C的調用,而非B直接返回到C。因此,目前大多數協程庫都只實現非對稱協程。

  • 一種是藉助glibc的ucontext,及相關的四個函數getcontext、setcontext、makecontext、swapcontext,如雲風的庫。當然這隻能在linux環境下使用,在windows下,可以藉助fiber實現類似的協程庫;
  • 利用C標準庫<setjmp.h>中的setjmp、longjmp實現協程。需要註意的是,setjmp僅負責保存寄存器的值,不負責維護其函數調用棧,這個需要另外實現;
  • 遵循規範從頭實現。如libaco,它支持 Intel386 和 x86-64 兩個平臺的Sys V ABI,並提供了非對稱協程的實現。關於Sys V ABI,It is today the standard ABI used by the major Unix operating systems such as Linux, the BSD systems, and many others. The Executable and Linkable Format (ELF) is part of the System V ABI. 也就是說,該協程庫只支持類unix系統;
  • 使用彙編實現。較為著名的是Boost庫,協程實現有兩套:Corountine2和Corountine。Corountine2在Boost v1.59被引入,Boost.Corountine目前已被標記為deprecated。Boost.Corountine2使用了Boost.Context,因此要使用Boost.Corountine2,必須先編譯Boost.Context。通用的C庫tbox的協程模塊也參照了Boost的實現。

關於彙編語法的平臺差異,類Unix下採用的是AT&T的彙編語法格式,Dos/Windows下麵採用的是Intel彙編語法格式。

 

參考資料:

雲風-coroutine源碼解析

System V ABI

 

轉載請註明本文出處:https://www.cnblogs.com/newton/p/11104187.html

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 工廠模式是使用頻率很高的一種設計模式,在面試中也經常問到,今天我們就來學習它。 為什麼要用工廠模式? 解答這個問題前,我們先來瞭解什麼是工廠模式。 工廠模式其實也稱創建模式,是用於創建對象的一種方式。本質上就是用工廠方法來代替new實例化對象。 舉個例子:我們在編寫代碼的時候,在一個A類中通過new ...
  • SpringCloud系列教程 | 第十篇:服務網關Zuul高級篇 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如無特殊說明,本系列教程全採用以上版本 上一篇我們主要聊到了Zuul的使用方式,以及自動轉發機制,其實Zuul還有更多的使用姿 ...
  • 架構雜談《一》 從傳統單體架構到服務化架構的發展歷程 典型的單體架構分為三個層級,Web層、業務邏輯層和數據存儲層,每個層的指責分別如下: Web 層:負責與用戶交互或者對外提供介面 業務邏輯層:為了實現業務邏輯而設計的流程處理模塊 數據存儲層:將業務邏輯層處理的結果持久化 將不同的模塊化組件聚合後 ...
  • (詳細)高校宿舍管理系統需求分析說明書(文末-->獲取原文檔) (詳細)高校宿舍管理系統需求分析說明書(文末-->獲取原文檔) 版本狀態 版本 作者 參與者 起止日期 註釋 審閱者 團隊 版本 日期 簽名 教學管理委員會 V1.1 2019.06.13 胡桂虹 教學管理委員會 V1.2 2019.0 ...
  • 1. 什麼是冪等性 冪等性就是指:一個冪等操作任其執行多次所產生的影響均與一次執行的影響相同。用數學的概念表達是這樣的: f(f(x)) = f(x).就像 nx1 = n 一樣, x1 就是一個冪等操作。無論是乘以多少次結果都一樣。 2. 常見的冪等性問題 冪等性問題經常會是由網路問題引起的,還有 ...
  • 分類整理一些內容,方便需要時回過頭來看,整理不易,如有疏漏,請多擔待!之後要查看這篇文章,公眾號後臺回覆 “設計模式聚合” 無靈魂,不模式。 設計模式是什麼鬼(初探) 設計模式是什麼鬼(原型) 設計模式是什麼鬼(單例) 設計模式是什麼鬼(適配器) 設計模式是什麼鬼(策略) 設計模式是什麼鬼(狀態) ...
  • SpringCloud系列教程 | 第九篇:服務網關Zuul初探 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如無特殊說明,本系列教程全採用以上版本 前面的文章我們介紹了,Eureka用於服務的註冊於發現,Feign支持服務的調用以及均衡 ...
  • 目前沒有系統學習過 Spring 框架,參與工作時,直接參与到了 Spring Boot 項目的開發。目前還比較菜,所以,你要是和我一樣,不妨也跳過 Spring 框架的學習,直接學習 Sring Boot。 官方文檔 的一段介紹: Spring Boot makes it easy to crea ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...