Heim >Backend-Entwicklung >Golang >Eine eingehende Analyse der Gründe, warum es in der Go-Sprache ein GMP-Planungsmodell gibt

Eine eingehende Analyse der Gründe, warum es in der Go-Sprache ein GMP-Planungsmodell gibt

青灯夜游
青灯夜游nach vorne
2023-04-14 15:26:341912Durchsuche

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!

Eine eingehende Analyse der Gründe, warum es in der Go-Sprache ein GMP-Planungsmodell gibt

Das GMP-Planungsmodell ist die Essenz von Go, das das Effizienzproblem von Multithread-Koroutinen für die gleichzeitige Planung angemessen löst.

Was ist GMP

Zunächst ist es notwendig zu verstehen, worauf sich die einzelnen GMP beziehen.

  • G: Die Abkürzung Goroutine bezieht sich auf Coroutine, die auf einem Thread läuft.
  • M: Die Abkürzung für Maschine, dh Kopf, Thread, zyklische Planungs-Coroutine und Ausführung.
  • P: Die Abkürzung für Prozessor bezieht sich auf den Prozessor, der Coroutinen in lokalen Warteschlangen speichert und Threads mit verfügbaren Coroutinen versorgt, die nicht schlafen,

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:

Eine eingehende Analyse der Gründe, warum es in der Go-Sprache ein GMP-Planungsmodell gibt

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.

Prozessor P

Die Struktur von Coroutine G und Thread M wurde in den vorherigen Artikeln erläutert. Hier ist eine Analyse von Prozessor P.

Funktion

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.

Quellcode-Analyse

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是一个储存协程的队列
  • runqheadrunqtail表示队列的头尾指针
  • runnext指向下一个可运行的协程

Eine eingehende Analyse der Gründe, warum es in der Go-Sprache ein GMP-Planungsmodell gibt

线程M与处理器P是如何协作的?

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中,感兴趣可以看看。

新建的协程该分配到哪?

新建的协程该分配到本地还是全局队列呢,得分情况:

  • Go认为新协程的优先级高,于是先寻找本地队列放入,而且还插队。
  • 本队队列满了才放入全局队列。

实际流程为:

  1. 随机寻找P
  2. 将新协程放入P的runnext中,意味着下一个就运行该协程,插队了
  3. 若P的协程满了,则放入全局队列

源码在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

Eine eingehende Analyse der Gründe, warum es in der Go-Sprache ein GMP-Planungsmodell gibt

Wie Thread M und Prozessor P zusammenarbeiten? #🎜🎜##🎜🎜##🎜🎜#In 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. #🎜🎜#

#🎜🎜#Wo soll die neu erstellte Coroutine zugeordnet werden? #🎜🎜##🎜🎜##🎜🎜#Soll die neu erstellte Coroutine der lokalen oder globalen Warteschlange zugewiesen werden? Ergebnis: #🎜🎜##🎜🎜##🎜🎜#Go ist der Ansicht, dass die neue Coroutine eine hohe Priorität hat . Also suchte ich zuerst nach einer lokalen Warteschlange und stellte sie ein, und sprang auch in die Schlange. #🎜🎜##🎜🎜#Die Warteschlange des Teams wird erst dann in die globale Warteschlange gestellt, wenn sie voll ist. #🎜🎜##🎜🎜##🎜🎜#Der eigentliche Prozess ist: #🎜🎜#
    #🎜🎜#Finde P zufällig#🎜🎜##🎜🎜#Füge die neue Coroutine in Ps 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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen