調度模型---MPG
image.png
- M(工作線程,它由系統(tǒng)調度): 一般比P個數多,做一些其他處理(如:runtime包的內置其他任務需要處理,當某個G發(fā)生系統(tǒng)調度產生阻塞時,多出來的M會接管剩余的本地G隊列),必須持有P才可以執(zhí)行代碼.
- P(處理器,包含運行go代碼的必要資源和調度
goroutine
的能力): 一般小于等于cpu
核數, 除了調度本地的runqueues
還會周期性的調用全局的global runqueue
- G(協(xié)程,由go關鍵字創(chuàng)建的): 分為兩個全局的
G
隊列(global runqueue
) 和每個P
自己維護的G
隊列(runqueues
)GO1.1
之前只有全局的G隊列,多個處理器P
通過互斥鎖調度,嚴重影響了并發(fā)執(zhí)行效率. 引入局部runqueues
后,每個處理器P
訪問自己的runqueuues
時不需要加鎖,大大提高了效率.
調度策略
- 隊列輪轉:
每個處理器P調度自己維護的隊列中的G到M中執(zhí)行,執(zhí)行結束則繼續(xù)調度下一個,另外會定期檢查global runqueue
中待運行的G并調度到M執(zhí)行,全局的global runqueue
主要來自從系統(tǒng)調用恢復的G.周期性檢測防止G的得不到調度機會. - 系統(tǒng)調用
當某個M所持有的P所調度的G,發(fā)生系統(tǒng)調用時,工作線程將會阻塞,即M將會阻塞,所以M個數要比P多,當發(fā)生阻塞時,在冗余的M中挑選一個接管P調度剩下的G,即做工作交接.當G系統(tǒng)調度結束后,如果有空閑的P,原M則會獲取一個P,繼續(xù)執(zhí)行G,否則將G放入全局的global runqueue
,并將M放入到緩存池. - 工作量竊取
即某個處理器P沒有需要調度的協(xié)程時,將從其他處理器中偷取一半的協(xié)程 - 搶占式調度
避免某個協(xié)程長時間執(zhí)行,而阻礙其他協(xié)程被調度的機制.在GO1.14之前如果協(xié)程沒有函數調用會無限的占用執(zhí)行權.如
go func() {
for {
// 無函數調用
}
}
直到GO1.14,調度器引入了基于信號的搶占機制,這個問題才得以解決.