なぜ Go には GMP スケジューリング モデルがあるのですか? Go言語にGMPスケジューリングモデルが存在する理由については、以下の記事で紹介していますので、ご参考になれば幸いです。
#GMP スケジューリング モデルは Go の本質であり、マルチスレッドの同時スケジューリング コルーチンの効率性の問題を合理的に解決します。
まず、各世代の GMP が何を指すのかを理解する必要があります。
一般的なプロセスは、スレッド M が P からコルーチンを取得することです。キュー コルーチンがロックを取得できない場合、グローバル キューからロックを取得するために競合します。
プロセッサ P
関数
ソース コード分析
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 ... }
m が属するスレッドです
runq
runqhead はキューの先頭ポインタと末尾ポインタを表します
runnext
スレッド M とプロセッサ P はどのように連携するのでしょうか?
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
にあります。興味があればご覧ください。新しく作成したコルーチンはどこに割り当てるべきですか?
Go は新しいコルーチンの優先度が高いと考えているため、最初にローカルキューに入れて列に並びます。
P
P のコルーチンがいっぱいの場合、グローバル キューに入れられます
// 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() } }) }
結論
Golang チュートリアル
以上がGo 言語に GMP スケジューリング モデルが存在する理由の詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。