再談協程

来源: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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...