Go為什麼要有GMP調度模型?以下這篇文章跟大家介紹一下Go語言中要有GMP調度模型的原因,希望對大家有幫助!
GMP調度模型是Go的精髓所在,它合理地解決了多執行緒並發調度協程的效率問題。
首先得清楚,GMP各代指什麼東西。
src\runtime\runtime2.go中,這裡展示部分重要欄位。
type p struct { ... m muintptr // back-link to associated m (nil if idle) // Queue of runnable goroutines. Accessed without lock. runqhead uint32 runqtail uint32 runq [256]guintptr runnext guintptr ... }
為處理器
p所屬的執行緒
是儲存協程的佇列
,
runqtail表示佇列的頭尾指標
指向下一個可執行的協程
src\runtime\proc.go中,有一個
schedule方法,這是執行緒運行的第一個函數。這函數中,執行緒需要取得到可運行的協程,程式碼如下:
func schedule() { ... // 寻找一个可运行的协程 gp, inheritTime, tryWakeP := findRunnable() ... }
func findRunnable() (gp *g, inheritTime, tryWakeP bool) { // 从本地队列中获取协程 if gp, inheritTime := runqget(pp); gp != nil { return gp, inheritTime, false } // 本地队列拿不到则从全局队列中获取协程 if sched.runqsize != 0 { lock(&sched.lock) gp := globrunqget(pp, 0) unlock(&sched.lock) if gp != nil { return gp, false, false } } }從本地佇列中取得協程
func runqget(pp *p) (gp *g, inheritTime bool) { next := pp.runnext // 队列中下一个可运行的协程 if next != 0 && pp.runnext.cas(next, 0) { return next.ptr(), true } ... }那麼如果本地佇列和全域佇列中都沒有協程了怎麼辦呢,難道就讓線程這麼閒著? 這時候處理器P就會任務竊取,從其他執行緒的本地佇列中竊取一些任務,美其名曰分擔其他執行緒的壓力,也提高了自己執行緒的使用率。 原始碼在
src\runtime\proc.go\stealWork中,感興趣可以看看。
中,意味著下一個就運行該協程,插隊了
src\runtime\proc.go \newproc函數中。
// Create a new g running fn. // Put it on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. func newproc(fn *funcval) { gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, gp, pc) // 创建新协程 pp := getg().m.p.ptr() runqput(pp, newg, true) // 寻找本地队列放入 if mainStarted { wakep() } }) }
以上是深入淺析Go語言中要有GMP調度模型的原因的詳細內容。更多資訊請關注PHP中文網其他相關文章!