Golang 協程排程

2022-03-16 04:58:02 字數 2371 閱讀 5137

下面看看golang的協程排程

groutine能擁有強大的併發實現是通過gpm排程模型實現,下面就來解釋下goroutine的排程模型。

go的排程器內部有三個重要的結構:m,p,g

m:m是對核心級執行緒的封裝,數量對應真實的cpu數,乙個m就是乙個執行緒,goroutine就是跑在m之上的;m是乙個很大的結構,裡面維護小物件記憶體cache(mcache)、當前執行的goroutine、隨機數發生器等等非常多的資訊

g:代表乙個goroutine,它有自己的棧,instruction pointer和其他資訊(正在等待的channel等等),用於排程。

p:p全稱是processor,處理器,它的主要用途就是用來執行goroutine的。每個processor物件都擁有乙個lrq(local run queue),未分配的goroutine物件儲存在grq(global run queue )中,等待分配給某乙個p的lrq中,每個lrq裡面包含若干個使用者建立的goroutine物件。

golang採用的是多執行緒模型,更詳細的說他是乙個兩級執行緒模型,但它對系統執行緒(核心級執行緒)進行了封裝,暴露了乙個輕量級的協程goroutine(使用者級執行緒)供使用者使用,而使用者級執行緒到核心級執行緒的排程由golang的runtime負責,排程邏輯對外透明。goroutine的優勢在於上下文切換在完全使用者態進行,無需像執行緒一樣頻繁在使用者態與核心態之間切換,節約了資源消耗。

排程實現

從上圖中看,有2個物理執行緒m,每乙個m都擁有乙個處理器p,每乙個也都有乙個正在執行的goroutine。

p的數量可以通過gomaxprocs()來設定,它其實也就代表了真正的併發度,即有多少個goroutine可以同時執行。

圖中灰色的那些goroutine並沒有執行,而是出於ready的就緒態,正在等待被排程。p維護著這個佇列(稱之為runqueue),

go語言裡,啟動乙個goroutine很容易:go function 就行,所以每有乙個go語句被執行,runqueue佇列就在其末尾加入乙個

goroutine,在下乙個排程點,就從runqueue中取出(如何決定取哪個goroutine?)乙個goroutine執行。

當乙個os執行緒m0陷入阻塞時(如下圖),p轉而在執行m1,圖中的m1可能是正被建立,或者從執行緒快取中取出。

當mo返回時,它必須嘗試取得乙個p來執行goroutine,一般情況下,它會從其他的os執行緒那裡拿乙個p過來,

如果沒有拿到的話,它就把goroutine放在乙個global

runqueue裡,然後自己睡眠(放入執行緒快取裡)。所有的p也會週期性的檢查global

runqueue並執行其中的goroutine,否則global runqueue上的goroutine永遠無法執行。

另一種情況是p所分配的任務g很快就執行完了(分配不均),這就導致了這個處理器p很忙,但是其他的p還有任務,此時如果global

runqueue沒有任務g了,那麼p不得不從其他的p裡拿一些g來執行。一般來說,如果p從其他的p那裡要拿任務的話,一般就拿run

queue的一半,這就確保了每個os執行緒都能充分的使用,如下圖:

m和p的數量如何確定?或者說何時會建立m和p?

1、p的數量:

2、m的數量:

m與p的數量沒有絕對關係,乙個m阻塞,p就會去建立或者切換另乙個m,所以,即使p的預設數量是1,也有可能會建立很多個m出來。

3、p何時建立:在確定了p的最大數量n後,執行時系統會根據這個數量建立n個p。

4、m何時建立:沒有足夠的m來關聯p並執行其中的可執行的g。比如所有的m此時都阻塞住了,而p中還有很多就緒任務,就會去尋找空閒的m,而沒有空閒的,就會去建立新的m。

m選擇哪乙個p關聯?

什麼時候會切換p與m的關聯關係?

當m因系統呼叫而阻塞時(m上執行的g進入了系統呼叫的時候),m與p會分開,如果此時p的就緒佇列中還有任務,

p就會去關聯乙個空閒的m,或者建立乙個m進行關聯。(也就是說go不是像libtask一樣處理io阻塞的?不確定。)

就緒的g如何選擇進入哪個p的就緒佇列?

如何保證每個p的就緒佇列中都會有g

如果乙個p的就緒佇列所有任務都執行完了,那麼p會嘗試從其他p的就緒佇列中取出一部分到自己的就緒佇列中,以保證每個p的就緒佇列都有任務可以執行。

Golang協程排程

有時候可能會出現這種情況,乙個無恥的goroutine阻止其他goroutine執行。當你有乙個不讓排程器執行的 for迴圈時,這就會發生。package main import fmt func main for done fmt.println done for迴圈並不需要是空的。只要它包含了不...

Golang 協程排程

n個使用者空間執行緒在1個核心空間執行緒上執行。優勢是上下文切換非常快但是無法利用多核系統的優點,多個使用者空間執行緒無法並行執行。1個核心空間執行緒執行乙個使用者空間執行緒。這種充分利用了多核系統的優勢但是上下文切換非常慢,因為每一次排程都會在使用者態和核心態之間切換。每個使用者執行緒對應多個核心...

golang 協程理解

本文總結一下go協程的理解,如有錯誤望請指正。網上都說協程是一種輕量級執行緒,執行緒又是一種輕量級的程序。這話在語言層面看來是沒有錯的,但它們的實現是不同的。執行緒是cpu資源排程的最小單位。協程不由cpu進行排程,由應用程式進行排程,也就是由go進行排程。在go中,協程的排程也有專門的排程器。但g...