Heim >Backend-Entwicklung >Golang >Eine eingehende Analyse der Gründe, warum es in der Go-Sprache ein GMP-Planungsmodell gibt
Warum verfügt Go über ein GMP-Planungsmodell? Der folgende Artikel stellt Ihnen die Gründe vor, warum es ein GMP-Planungsmodell in der Go-Sprache gibt. Ich hoffe, er wird Ihnen hilfreich sein!
Das GMP-Planungsmodell ist die Essenz von Go, das das Effizienzproblem von Multithread-Koroutinen für die gleichzeitige Planung angemessen löst.
Zunächst ist es notwendig zu verstehen, worauf sich die einzelnen GMP beziehen.
Threads M enthält jeweils einen Prozessor P. Beim Erwerb von Coroutinen werden diese zunächst von P bezogen. Daher lautet das GMP-Modelldiagramm wie folgt:
Das Allgemeine Prozess Ja, Thread M erhält die Coroutine aus der Warteschlange von P. Wenn er sie nicht erhalten kann, konkurriert er um die Sperre aus der globalen Warteschlange, um sie zu erhalten.
Die Struktur von Coroutine G und Thread M wurde in den vorherigen Artikeln erläutert. Hier ist eine Analyse von Prozessor P.
Prozessor P speichert einen Stapel von Coroutinen, sodass Thread M Coroutinen von ihnen erhalten kann, ohne zu sperren, ohne mit ihnen interagieren zu müssen Andere Threads konkurrieren um Coroutinen in der globalen Warteschlange und verbessern so die Effizienz der Coroutinenplanung.
pDer Quellcode der Struktur befindet sich in srcruntimeruntime2.go
und einige wichtige Felder werden hier angezeigt. srcruntimeruntime2.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 ... }
m
为处理器p
所属的线程runq
是一个储存协程的队列runqhead
,runqtail
表示队列的头尾指针runnext
指向下一个可运行的协程在srcruntimeproc.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就会任务窃取,从其他线程的本地队列中窃取一些任务,美其名曰分担其他线程的压力,还提高了自己线程的利用率。
源码在srcruntimeproc.gostealWork
中,感兴趣可以看看。
新建的协程该分配到本地还是全局队列呢,得分情况:
实际流程为:
runnext
中,意味着下一个就运行该协程,插队了源码在srcruntimeproc.gonewproc
// 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() } }) }
m
ist der Thread, zu dem Prozessor p
gehört runq code >Ist eine Warteschlange, die Coroutinen speichert
runqhead
, runqtail
stellt die Kopf- und Endzeiger der Warteschlange dar
runnext
zeigt auf die nächste ausführbare Coroutine
srcruntimeproc.go
gibt es eine schedule
-Methode, die die erste Funktion ist, die vom Thread ausgeführt wird. In dieser Funktion muss der Thread eine ausführbare Coroutine erhalten. Der Code lautet wie folgt: #🎜🎜#rrreeerrreee#🎜🎜#Holen Sie sich die Coroutine aus der lokalen Warteschlange #🎜🎜#rrreee#🎜🎜#Wenn die lokale Warteschlange und die globale Warteschlange Was sollen wir tun, wenn es keine Coroutinen gibt? Sollen wir die Threads einfach so im Leerlauf lassen? #🎜🎜##🎜🎜#Zu diesem Zeitpunkt stiehlt Prozessor P Aufgaben und stiehlt einige Aufgaben aus den lokalen Warteschlangen anderer Threads. Dies wird als Aufteilen des Drucks anderer Threads und Verbessern der Auslastung seiner eigenen Threads bezeichnet. #🎜🎜##🎜🎜#Der Quellcode befindet sich in srcruntimeproc.gostealWork
, bei Interesse können Sie einen Blick darauf werfen. #🎜🎜#runnext bedeutet, dass die Coroutine als nächstes ausgeführt und in die Warteschlange gestellt wird Der Code befindet sich in der Funktion <code>srcruntimeproc.gonewproc
. #🎜🎜#rrreee#🎜🎜##🎜🎜#Fazit#🎜🎜##🎜🎜##🎜🎜#In diesem Artikel wird zunächst das GMP-Planungsmodell vorgestellt und detailliert beschrieben, wie Prozessor P und Thread M Coroutinen erhalten. #🎜🎜##🎜🎜#Prozessor P löst das Problem des gegenseitigen Ausschlusses mehrerer Threads, um Coroutinen zu erhalten, und verbessert die Effizienz der Planung von Coroutinen. Unabhängig davon, ob sich Coroutinen in lokalen oder globalen Warteschlangen befinden, scheint dies jedoch nur der Fall zu sein Nacheinander ausgeführt, also Wie implementiert man die asynchrone und gleichzeitige Ausführung von Coroutinen? Lassen Sie uns die Analyse im nächsten Artikel fortsetzen (obwohl ihn niemand lesen wird ...). #🎜🎜##🎜🎜#Empfohlenes Lernen: #🎜🎜#Golang-Tutorial#🎜🎜##🎜🎜#
Das obige ist der detaillierte Inhalt vonEine eingehende Analyse der Gründe, warum es in der Go-Sprache ein GMP-Planungsmodell gibt. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!