本文將會描述golang排程的大致方式,並介紹後文中將會用到的一些資料結構,及實現原理
注:這本是給自己學習用的筆記,其中包含一些網上大牛的資料節選片段,也忘了出處,求海涵。大佬不願意可以聯絡刪除謝謝。
主要參考:《go語言學習筆記》
一般多執行緒會有以下幾種執行緒模型:
go採用方案:採用兩級(混合型)執行緒模型,通過m:n的排程器去獲取這兩個世界的全部優勢,在任意數目的os執行緒上呼叫任意數目的goroutines。
golang 排程模型架構如下圖:
在golang的原始碼結構中,排程過程可用下圖展示:
結合兩個圖我們可以分析出幾個結論:
我們通過 go func()來建立乙個goroutine;
有兩個儲存goroutine的佇列,乙個是區域性排程器p的local queue(當p繫結的時候m的時候,可以無鎖分配記憶體和無鎖訪問任務佇列)、乙個是全域性排程器資料模型schedt的global queue。新建立的goroutine會先儲存在local queue,如果local queue已經滿了就會儲存在全域性的global queue;
goroutine只能執行在m中,乙個m必須持有乙個p,m與p是1:1的關係。m會從p的local queue彈出乙個runable狀態的goroutine來執行,如果p的local queue為空,就會執行work stealing;
乙個m排程goroutine執行的過程是乙個loop;
當m執行某乙個goroutine時候如果發生了syscall或則其餘阻塞操作,m會阻塞,如果當前有一些g在執行,runtime會把這個執行緒m從p中摘除(detach),然後再建立乙個新的作業系統的執行緒(如果有空閒的執行緒可用就復用空閒執行緒)來服務於這個p;
當系統呼叫結束時候,這個goroutine會嘗試獲取乙個空閒的p執行,並放入到這個p的local queue。如果獲取不到p,那麼這個執行緒m會park它自己(休眠), 加入到空閒執行緒中,然後這個goroutine會被放入schedt的global queue。
gpm簡介(具體結構見下文資料結構):
下文介紹排程其中會使用到的部分基本資料結構,原始碼中的資料結構在runtime包中的runtime2.go中
stack 描述了go執行堆疊。堆疊的邊界正好是 [lo,hi),兩邊都沒有隱式資料結構。
type stack struct
g 的執行現場,建立時初始化,用來儲存g當前的執行狀況,g被搶占是會儲存當前現場,下次排程的時候從這裡恢復
type gobuf struct
type g struct
g裡面比較重要的成員如下
g的狀態:
當 g 遇到阻塞,或需要等待的場景時,會被打包成 sudog 這樣乙個結構。乙個 g 可能被打包為多個 sudog 分別掛在不同的等待佇列上:
// sudog 代表在等待列表裡的 g,比如向 channel 傳送/接收內容時
// 之所以需要 sudog 是因為 g 和同步物件之間的關係是多對多的
// 乙個 g 可能會在多個等待佇列中,所以乙個 g 可能被打包為多個 sudog
// 多個 g 也可以等待在同乙個同步物件上
// 因此對於乙個同步物件就會有很多 sudog 了
// sudog 是從乙個特殊的池中進行分配的。用 acquiresudog 和 releasesudog 來分配和釋放 sudog
type sudog struct
執行緒在 runtime 中的結構,對應乙個 pthread,pthread 也會對應唯一的核心執行緒(task_struct):
type m struct
m裡面比較重要的成員如下
m並沒有像g和p一樣的狀態標記, 但可以認為乙個m有以下的狀態:
m可以執行兩種**:
自旋中(spinning)這個狀態非常重要, 是否需要喚醒或者建立新的m取決於當前自旋中的m的數量.通常建立乙個m的原因是由於沒有足夠的m來關聯p並執行其中可執行的g。而且執行時系統執行系統監控的時候,或者gc的時候也會建立m。
抽象資料結構,可以認為是 processor 的抽象,代表了任務執行時的上下文,m 必須獲得 p 才能執行:
type p struct
p裡面比較重要的成員如下
p的狀態
全域性排程器,全域性只有乙個 schedt 型別的例項:
type schedt struct
sap生產排程綜述
sap生產排程綜述 文章摘要 sap生產排程綜述 做什麼都需要計畫,製造企業的生產更是如此。企業做出銷售 或是接到客戶訂單時,必須快速做出反映,能不能在要求的期間內完成訂單,在什麼時候完成,完成訂單需要哪些物料,庫存情況怎樣,哪些需要購買,哪些自已生產,在生產之前材料是否能準時到達工廠。企業內.sa...
Golang排程器GMP學習筆記(二)
我們通過 go func 來建立乙個goroutine 有兩個儲存g的佇列,乙個是區域性排程器p的本地佇列 乙個是全域性g佇列。新建立的g會先儲存在p的本地佇列中,如果p的本地佇列已經滿了就會儲存在全域性的佇列中 g只能執行在m中,乙個m必須持有乙個p,m與p是1 1的關係。m會從p的本地佇列彈出乙...
Golang排程器GMP學習筆記(一)
單程序時代的問題 多程序 多執行緒的問題 協程的問題m goroutine協程 n thread執行緒 m想要執行 放回g都必須訪問全域性g佇列,並且m有多個,即多執行緒訪問同一資源需要加鎖進行保證互斥 同步,所以全域性g佇列是有互斥鎖進行保護的。問題 建立 銷毀 排程g都需要每個m獲取鎖,這就形成...