This page looks best with JavaScript enabled

Concurrency in Go IV 第六章 Goroutines and the Go runtime

 ·  ☕ 3 min read

這章節是概略介紹一下 Go 的 scheduler 如何運作的~
有些事之前有看過
不過 tasks 和 continuation 真的是第一次聽到,滿有趣的概念

Work Stealing

Fair scheduling,直接把一堆任務平均分散到各個 processor 上
但因為 Go 是使用 fork-join,一個 goroutine 是從一個 goroutine 分出去的,常常會互相依賴(順序問題),可能造成各個 processor 使用不均(每個task loading不同)。且 cache 也可能有效率差的問題,如果相關 task 分散各個 processor。

A centralized task queue?

如果引進“一個” task queue 呢?
負責裝所有任務,有空的 processor 就去拿任務來做
是可以解決一些 loading 分配不均的問題,但大家都要去存取這個唯一 task queue,進出 critical section 很耗費時間。且 cache 問題也沒解決,不知道哪個 task 會去哪個 processor 上

Multiple queue?

再進一步,給每個 processor 都給一個 local queue 呢?
解決的 critical section 的問題了
但如果 fork 出來的 goroutine 都在同一個 P 上,要怎麼搬去其他 P,還有 context switch 怎麼做??

Work stealing

  1. fork 時,加入該 thread 的 queue 的尾端
  2. thread 沒事時,去別人的 queue 頭端偷工作
  3. 當 join point 到達,要等待其他人回應結果才能繼續時,這個 thread 會先暫停這個工作,並從自己的 queue 的尾端拿工作出來做
  4. thread 的 queue 是空的時候,可能會停在 join point 或是去別人的頭端偷工作來做

可以注意的是 1 和 3,都是從尾端操作
因為尾端的任務是最後 push 進去的,最有可能是 parent 要 join 所需要的結果,快點 join 效能好
因為是達到 join point 前最後處理的東西(尾端工作),所以最有可能還在 cache 中

Stealing Tasks or Continuations?

假設目前正在執行的 goroutine 叫做 A,而 A 啟動了一個 goroutine B,那此時目前的 thread 會選擇 A 剩下的內容(continuation)或是 B (task) 繼續做呢?

答案是通常會把 task 拿來做,而把 A 推進 local queue 的尾端
原因是

  1. 通常程式都是希望中間 fork 出去的 goroutine 做完一些事情,然後 join 回來原本的 goroutine
  2. 原本的 gorutine fork 出別的 gorutine 後過不久通常會卡住等 join

如果 thread 繼續做A,那通常就會是 (A 做到 join point) -> (B 做完) -> (繼續A)
如果是做 B,那通常會是 (B 做完) -> (繼續A)
少了一個 context switch

注意 fork 時選擇 A 或 B 都不算 context switch,只是選擇其中之一丟進 queue 而已

continuation-stealing 指的就是在 fork 時,是把原本的 goroutine 剩下部分都進 queue 內,這種方法被認為是比較好的,蛋需要特別的 compiler 支援,而 Go 有

GMP model

G 代表 goroutine 的狀態,尤其是 program counter

接下來的一小段其實就是在說 shceduler 怎麼運作,跟之前寫過的一篇文章類似
M(thread) 至少會有跟 P 一樣的數量,當某個 thread 被 G 卡住(讀IO之類的),這個 M 就會跟 P 切開,自己去旁邊等,讓 P 可以去跟別的 M 結合,繼續使用 CPU
Global run queue 則是為了他們的實作細節優化,我就不知道詳細了XD

最後他寫了目前如果一個 goroutine 沒有斷點(function call、io那些)的話是無法被搶奪的,會造成問題
這個實際上我在工作時就遇過XD
有一個 goroutine 裡面跑了無窮迴圈,好像導致垃圾回收把其他人都停止之後一直在等這個 g 等不完就爆了

local 測試時測不出來,後來發現原來是新版的 Go 已經把問題解掉,好像是1.14(?
爆掉的那台機器好像是用比較舊的某版才會發生,真4神奇

Share on

Marko Peng
WRITTEN BY
Marko Peng
Good man