ホームページ  >  記事  >  バックエンド開発  >  Go 言語に GMP スケジューリング モデルが存在する理由の詳細な分析

Go 言語に GMP スケジューリング モデルが存在する理由の詳細な分析

青灯夜游
青灯夜游転載
2023-04-14 15:26:341851ブラウズ

なぜ Go には GMP スケジューリング モデルがあるのですか? Go言語にGMPスケジューリングモデルが存在する理由については、以下の記事で紹介していますので、ご参考になれば幸いです。

Go 言語に GMP スケジューリング モデルが存在する理由の詳細な分析

#GMP スケジューリング モデルは Go の本質であり、マルチスレッドの同時スケジューリング コルーチンの効率性の問題を合理的に解決します。

GMP とは

まず、各世代の GMP が何を指すのかを理解する必要があります。

  • G: Goroutine の略語は、スレッド上で実行されるコルーチンを指します。
  • M: Machine の略称。つまり、thead、thread、cyclic scheduling coroutine、execution のことです。
  • P: Processor の略語で、コルーチンをローカル キューに保存し、スレッドに休止状態ではない利用可能なコルーチンを提供するプロセッサを指します。プロセッサ P はコルーチンを取得したいと考えています。コルーチンはまず P から取得されるため、GMP モデル図は次のようになります。

一般的なプロセスは、スレッド M が P からコルーチンを取得することです。キュー コルーチンがロックを取得できない場合、グローバル キューからロックを取得するために競合します。 Go 言語に GMP スケジューリング モデルが存在する理由の詳細な分析

プロセッサ P

コルーチン G とスレッド M の構造については以前の記事で説明しましたが、ここではプロセッサ P を分析します。

関数

プロセッサ P はコルーチンのバッチを保存するため、スレッド M は、グローバル キューをめぐって他のスレッドと競合することなく、ロックせずにコルーチンからコルーチンを取得できます。 . プロセス内のコルーチンにより、コルーチンのスケジューリング効率が向上します。

ソース コード分析

p 構造体のソース コードは 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
   ...
}

m
    はプロセッサ
  • p が属するスレッドです runq
  • は格納するキューですコルーチン
  • runqhead
  • runqtail はキューの先頭ポインタと末尾ポインタを表します runnext
  • は次の実行可能なコルーチンを指します

Go 言語に GMP スケジューリング モデルが存在する理由の詳細な分析スレッド M とプロセッサ P はどのように連携するのでしょうか?

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

にあります。興味があればご覧ください。

新しく作成したコルーチンはどこに割り当てるべきですか?

新しく作成されたコルーチンはローカル キューまたはグローバル キューに割り当てられるべきですか? スコア:

Go は新しいコルーチンの優先度が高いと考えているため、最初にローカルキューに入れて列に並びます。

    このチームのキューがいっぱいになると、グローバル キューに入れられます。
  • 実際のプロセスは次のとおりです。

P

    P をランダムに検索します。新しいコルーチンを P の
  1. runnext
  2. に入れます。これは、次にコルーチンが実行され、キューがジャンプされます。
  3. P のコルーチンがいっぱいの場合、グローバル キューに入れられます
  4. ソース コードは
  5. 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()
      }
   })
}
結論

この記事では、最初に GMP スケジューリング モデルを紹介し、特にプロセッサ P とスレッド M がコルーチンを取得する方法を紹介します。

プロセッサ P は、コルーチンを取得するためのマルチスレッド排他の問題を解決し、コルーチンのスケジューリングの効率を向上させますが、コルーチンがローカル キューにあるかグローバル キューにあるかに関係なく、コルーチンは順次実行されるだけのようです。では、Go ではどうすればよいでしょうか? コルーチンの非同期および同時実行を実装するにはどうすればよいでしょうか?次の記事で分析を続けましょう(誰も読んでくれないでしょうが…)。

推奨学習:

Golang チュートリアル

以上がGo 言語に GMP スケジューリング モデルが存在する理由の詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。