Golang 協程
Golang 協程
1. CSP 併發模型
Communication Sequential Processes
Do not communicate by sharing memory; instead, share memory by communicating.
不要通過共享記憶體來通訊,而要通過通訊實現記憶體共享
Go的CSP併發模型,通過goroutine和channel來實現:
-
goroutine: 併發的執行單位
Goroutine是Golang併發的實體,它底層使用協程實現併發,coroutine是一種執行在使用者態的使用者執行緒,類似greenthread,具有如下特點:
- 使用者空間,避免了核心態和使用者態的切換導致的成本
- 可由語言和框架層進行排程
- 更小的棧空間允許建立大量的例項
-
channel: 併發的通訊機制
channel被單獨建立,可以在程式之間傳遞,一個實體通過將訊息發到channel中,然後又監聽這個channel的實體處理,兩個實體之間是匿名的,它實現了實體中間的解藕。
2. 協程、執行緒、程式
-
程式:系統進行資源分配和排程的一個獨立單位。每個程式都有自己獨立的記憶體空間,不同程式通過IPC通訊。程式上下文切換開銷(棧、暫存器、虛擬記憶體、檔案控制程式碼)比較大,但相對比較穩定安全。
-
執行緒:處理器排程和分配的基本單位。執行緒是程式內部的一個執行單元,每個程式至少有一個主執行緒,它無需使用者去主動建立,由系統自動建立。執行緒間通訊主要通過共享記憶體、上下文切換較快,資源開銷小,但相比程式不夠穩定,容易丟資料
-
協程:使用者態輕量級執行緒,它的排程完全由使用者控制。協程擁有自己的暫存器上下文和棧。協程排程切換時,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧。開銷小,切換快。
程式、執行緒、協程的關係和區別:
- 程式:擁有獨立的堆和棧,由作業系統排程
- 執行緒:擁有獨立的棧,但共享堆。由作業系統排程(標準執行緒)
- 協程:和執行緒一樣,擁有獨立的棧,共享堆。由程式開發者在程式碼中顯示排程
為什麼協程比執行緒輕量?
- 記憶體消耗極小:
- goroutine: 2KB, 如果棧記憶體不足,自動擴容
- thread: 1M, 另外還需要一個"a guard page"區域用於與其他執行緒的棧空間進行隔離
- 建立和銷燬損耗資源小:
- goroutine: 由Go runtime負責管理,建立和銷燬的消耗非常小,是使用者級別的。
- thread: 是核心級的,建立和銷燬都會有巨大的消耗,一般通過執行緒池來緩解
- 切換快:
- goroutine: 不依賴於系統,由golang自己實現的CSP併發模型實現:G-P-M。go協程也叫使用者態執行緒,協程的切換髮生在使用者態,約為200ns
- thread: 核心對外提供的服務,應用程式可以通過系統呼叫讓核心啟動執行緒。執行緒在等待IO操作時變成unrunnable狀態觸發上下文切換,1000~1500ns
3. 併發的實現原理
KSE:Kernel Scheduling Entity, 核心排程實體,即可以被作業系統核心排程器排程的實體物件,它是核心的最小排程單元,也就是核心級執行緒
三種執行緒模型:
- 使用者級執行緒模型
- 核心級執行緒模型
- 兩級執行緒模型(即混合型執行緒模型)
3.1 使用者級執行緒模型
多個使用者態的執行緒對應一個核心執行緒,執行緒的建立、終止、切換或同步等工作必須自身來完成;
優點:執行緒排程在使用者層面完成,不存在CPU在使用者態和核心態之間切換,輕量級,對系統資源消耗少
缺點:做不到真正意義上的併發。如果存在某個執行緒阻塞呼叫(比如I/O操作),其他執行緒將被阻塞,整個程式被掛起。因為在使用者執行緒模式下,程式內的執行緒繫結到CPU是由使用者程式排程實現的,內部執行緒對CPU不可見,即CPU排程的是程式,而非執行緒
Python協程庫gevent,把阻塞的操作重新封裝為完成給阻塞模式,在阻塞點上,主動讓出自己,並通知或喚醒其他等待的使用者執行緒。
3.2 核心級執行緒模型
直接呼叫作業系統的核心執行緒,所有執行緒的建立、終止、切換、同步等操作,都由核心完成
優點:簡單。直接藉助核心的執行緒和排程器,可以快速實現執行緒切換,做到真正的併發處理
缺點:直接使用核心區建立、銷燬及執行緒上下文切換和排程,系統資源開銷大,影響效能
Java/C++ 執行緒庫
3.3 兩級執行緒模型(即混合型執行緒模型)
一個程式可與多個核心執行緒KSE關聯,該程式內的多個執行緒繫結到了不同的KSE上
程式內的執行緒並不與KSE一一繫結,當某個KSE繫結的執行緒因阻塞操作被核心排程出CPU時,其關聯的程式中的某個執行緒又會重新與KSE繫結
為什麼稱為兩級?使用者排程實現使用者執行緒到KSE的排程,核心排程器實現KSE到CPU上的排程
Golang
4. G-P-M 模型
G-P-M 模型:
- G:Goroutine:獨立執行單元。相較於每個OS執行緒固定分配2M記憶體的模式,Goroutine的棧採取動態擴容方式,2k ~ 1G(AMD64, AMD32: 256M)。週期性回收記憶體,收縮棧空間
- 每個Goroutine對應一個G結構體,它儲存Goroutine的執行堆疊、狀態及任務函式,可重用。
- G並非執行體,每個G需要繫結到P才能被排程執行
- P:Processor: 邏輯處理器,中介
- 對G來說,P相當於CPU,G只有繫結到P才能被呼叫
- 對M來說,P提供相關的執行環境(Context),如記憶體分配狀態(mcache),任務佇列(G)等
- P的數量決定系統最大並行的G的數量 (CPU核數 >= P的數量),使用者可通過GOMAXPROCS設定數量,但不能超過256
- M:Machine
- OS執行緒抽象,真正執行計算的資源,在繫結有效的P後,進入schedule迴圈
- schedule迴圈的機制大致從Global佇列、P的Local佇列及wait佇列中獲取G,切換到G的執行棧上執行G的函式,呼叫goexit做清理工作並回到M
- M不保留G的狀態
- M的數量不定,由Go Runtime調整,目前預設不超過10K
5. Golang 併發控制模型
- channel
- sync.WaitGroup:
Add(), Done(), Wait()
- Context:
Done(), Err(), Deadline(), Value()
較複雜的併發
6. gorountine排程時機
情形 | 說明 |
---|---|
go | go 建立一個新的 goroutine,Go scheduler 會考慮排程 |
GC | 由於進行 GC 的 goroutine 也需要在 M 上執行,因此肯定會發生排程。當然,Go scheduler 還會做很多其他的排程,例如排程不涉及堆訪問的 goroutine 來執行。GC 不管棧上的記憶體,只會回收堆上的記憶體 |
系統呼叫 | 當 goroutine 進行系統呼叫時,會阻塞 M,所以它會被排程走,同時一個新的 goroutine 會被排程上來 |
記憶體同步訪問 | atomic,mutex,channel 操作等會使 goroutine 阻塞,因此會被排程走。等條件滿足後(例如其他 goroutine 解鎖了)還會被排程上來繼續執行 |
相關文章
- golang協程池設計Golang
- Golang協程池(workpool)實現Golang
- Golang —— goroutine(協程)和channel(管道)Golang
- golang學習筆記(二)—— 深入golang中的協程Golang筆記
- Golang協程併發的流水線模型Golang模型
- golang 多協程的同步方法總結Golang
- golang: 用協程非同步寫日誌Golang非同步
- Golang 的 協程排程機制 與 GOMAXPROCS 效能調優Golang
- GoLang協程Goroutiney原理與GMP模型詳解Golang模型
- ip代理-Golang協程批次檢測代理線路Golang
- 5. 併發神奇——協程 |《 刻意學習 Golang 》Golang
- 探索Golang協程實現——從v1.0開始Golang
- Golang協程無法固定goroutine的最大數目解決Golang
- 【Golang】淺談協程併發競爭資源問題Golang
- Golang語言goroutine協程併發安全及鎖機制Golang
- 【協程原理】 - Java中的協程Java
- swoole 協程原始碼解讀 (協程的排程)原始碼
- Swoole 協程與 Go 協程的區別Go
- Kotlin Coroutine(協程): 二、初識協程Kotlin
- Kotlin Coroutine(協程): 三、瞭解協程Kotlin
- Swoole協程與Go協程的區別Go
- PHP 協程PHP
- 協程初探
- Golang的演化歷程Golang
- swoole 協程原始碼解讀 (協程的建立)原始碼
- python 協程與go協程的區別PythonGo
- Python協程與JavaScript協程的對比PythonJavaScript
- python 協程Python
- Python協程Python
- swoole協程初探
- 30. 協程
- 協程asyncio 模組
- 【譯】kotlin 協程官方文件(1)-協程基礎(Coroutine Basics)Kotlin
- Go 併發 -- 協程Go
- 非同步與協程非同步
- Python的協程Python
- python中協程Python
- Kotlin的協程Kotlin