做者:林冠宏 / 指尖下的幽靈git
GitHub : https://github.com/af913337456/併發
騰訊雲專欄: https://cloud.tencent.com/developer/user/1148436/activities函數
正確地認識 G , M , P 三者的關係,可以對協程的調度機制有更深刻的理解! 本文將會完整介紹完 go 協程的調度機制,包含:性能
- 調度對象的主要組成
- 各對象的關係 與 分工
- gorutine 協程是如何被執行的
- 內核線程 sysmon 對 gorutine 的管理
- gorutine 協程中斷掛起 與 恢復
- GOMAXPROCS 如何影響 go 的併發性能
Golang 簡稱 Go,Go 的協程(goroutine)
和咱們常見的線程(Thread)
同樣,擁有其調度器。spa
go 關鍵詞
時候會建立的一個對象處理器
,又稱上下文隊列
隊列中
提取 G,並執行GOMAXPROCS
(最大256),啓動時固定的,通常不修改協程任務
交換全局隊列
找gorutine
,它會加入到本地隊列或者全局隊列空隊列
一個點
!
若是一個G任務執行時間太長,它就會一直佔用 M 線程,因爲隊列的G任務是順序執行的,其它G任務就會阻塞,如何避免該狀況發生? --①線程
底層線程
,循環執行
能找到的 G 任務。這裏的尋找的 G 從下面幾方面找:
協程的切換時間片是10ms,也就是說 goroutine 最多執行10ms就會被 M 切換到下一個 G。這個過程,又被稱爲 中斷,掛起
code
原理:協程
go程序啓動時會首先建立一個特殊的內核線程 sysmon
,用來監控和管理,其內部是一個循環:
記錄全部 P 的 G 任務的計數 schedtick
,schedtick會在每執行一個G任務後遞增
若是檢查到 schedtick
一直沒有遞增,說明這個 P 一直在執行同一個 G 任務,若是超過10ms,就在這個G任務的棧信息裏面加一個 tag 標記
而後這個 G 任務在執行的時候,若是遇到非內聯函數調用,就會檢查一次這個標記,而後中斷本身,把本身加到隊列末尾,執行下一個G
若是沒有遇到非內聯函數
調用的話,那就會一直執行這個G任務,直到它本身結束;若是是個死循環,而且 GOMAXPROCS=1 的話。那麼一直只會只有一個 P 與一個 M,且隊列中的其餘 G 不會被執行!
例子,下面的這段代碼,hello world
不會被輸出
func main(){ runtime.GOMAXPROCS(1) go func(){ fmt.Println("hello world") // panic("hello world") // 強制觀察輸出 }() go func(){ for { // fmt.Println("aaa") // 非內聯函數,這行註釋打開,將致使 hello world 的輸出 } }() select {} }
看完上面的內容,相信你已經知道,GOMAXPROCS
就是 go 中 runtime 包的一個函數。它設置了 P 的最多的個數。這也就直接致使了 M 最多的個數是多少,而 M 的個數就決定了各個 G 隊列能同時被多少個 M 線程來進行調取執行!
故,咱們通常將 GOMAXPROCS 的個數設置爲 CPU 的核數,且須要注意的是: