Golang コルーチンのスケジューリングについて

藏色散人
リリース: 2020-09-01 14:50:02
転載
2680 人が閲覧しました

次のコラムでは、golang チュートリアル コラムで Golang コルーチンのスケジューリングについて紹介します。困っている友人のお役に立てれば幸いです。

Golang コルーチンのスケジューリングについて

1. スレッド モデル

  • N:1 モデル、N 個のユーザー空間スレッドが 1 つのカーネル空間スレッドで実行されます。利点は、コンテキストの切り替えが非常に高速であることですが、マルチコア システムを活用できないことです。
  • 1:1 モデル、1 つのカーネル空間スレッドは 1 つのユーザー空間スレッドを実行します。これはマルチコア システムの利点を最大限に活用しますが、スケジュールごとにユーザー モードとカーネル モードの間で切り替わるため、コンテキストの切り替えが非常に遅くなります。 (POSIX スレッド モデル (pthread)、Java)
  • M:N モデルでは、各ユーザー スレッドは複数のカーネル空間スレッドに対応し、1 つのカーネル空間スレッドは複数のユーザー空間スレッドに対応することもできます。 Go はこのモデルを採用し、任意の数のカーネル モデルを使用して任意の数のゴルーチンを管理する予定です。これは上記の 2 つのモデルの利点を組み合わせたものですが、欠点はスケジュールが複雑になることです。

golang のコルーチン スケジューリングを見てみましょう

  • M: ユーザー空間スレッドは、posix pthread
  • P と同様に、カーネル スレッドに対応します。前のセクションで実装したスケジューラである実行コンテキストを表します。スケジューラは、レディ キュー
  • G: goroutine、つまり coroutine

2 にも対応します。スケジューリング モデルの概要

Groutine では、GPM スケジューリング モデルを通じて強力な同時実行性を実装できます。次に、Groutine スケジューリング モデルについて説明します。

Go のスケジューラには、内部に 3 つの重要な構造があります: M、P、G
M: M はカーネルレベルのスレッドのカプセル化であり、数値は実数に対応します。 CPU の M はスレッドであり、ゴルーチンは M 上で実行されます。M は、スモール オブジェクト メモリ キャッシュ (mcache)、現在実行中のゴルーチン、乱数ジェネレーターなどの多くの情報を保持する大きな構造です。
G : スケジューリングのための独自のスタック、命令ポインタ、その他の情報 (待機チャネルなど) を持つゴルーチンを表します。
P: P の正式名は Processor で、主な目的は goroutine を実行することです。各プロセッサ オブジェクトには LRQ (ローカル実行キュー) があります。未割り当ての Goroutine オブジェクトは GRQ (グローバル実行キュー) に保存され、LRQ 内の特定の P に割り当てられるのを待ちます。各 LRQ にはユーザーが作成した複数の Goroutine オブジェクトが含まれます。

Golang はマルチスレッド モデルを使用します。詳しく説明すると、これは 2 レベルのスレッド モデルですが、システム スレッド (カーネル レベルのスレッド) をカプセル化し、軽量のコルーチン goroutine を公開します。(ユーザー レベルのスレッド) ) はユーザーが使用できるもので、ユーザーレベルのスレッドからカーネルレベルのスレッドへのスケジューリングは golang のランタイムによって処理され、スケジューリング ロジックは外部に対して透過的です。 goroutine の利点は、コンテキストの切り替えが完全なユーザー状態で実行されることであり、スレッドほど頻繁にユーザー状態とカーネル状態を切り替える必要がないため、リソースの消費が節約されます。

スケジューリングの実装

上の図からわかるように、2 つの物理スレッド M があり、各 M にはプロセッサ P があり、それぞれに goroutine があります。ランニング。
P の数は GOMAXPROCS() を通じて設定できます。これは実際の同時実行性、つまり同時に実行できるゴルーチンの数を表します。
画像内の灰色のゴルーチンは実行されていませんが、準備完了状態にあり、スケジュールされるのを待っています。 P はこのキュー (runqueue と呼ばれます) を維持します。
Go 言語では、go 関数を実行するだけで goroutine を開始するのが簡単です。そのため、go ステートメントが実行されるたびに、
goroutine が runqueue の最後に追加されます。 queue. 、次のスケジューリング ポイントで、runqueue から goroutine が取り出され (どの goroutine を取得するかを決定する方法は?)、実行されます。

OS スレッド M0 がブロックされている場合 (以下に示すように)、P は代わりに M1 を実行しています。図内の M1 は、スレッド キャッシュから作成されているか、スレッド キャッシュから取り出されている可能性があります。

MO が戻ったら、ゴルーチンを実行するために P を取得しようとする必要があります。通常は、他の OS スレッドから P を取得します。
それを取得できない場合は、ゴルーチンをグローバルに置きます runqueue を実行し、その後単独でスリープします (スレッド キャッシュに入れられます)。すべてのPも定期的にグローバルをチェックします runqueue を作成し、その中で goroutine を実行します。そうしないと、グローバル runqueue 上の goroutine は実行されません。

もう 1 つの状況は、P によって割り当てられたタスク G がすぐに完了する (不均一な分散) ため、プロセッサ P は非常にビジー状態になりますが、他の P は依然としてビジー状態です。この時点でグローバルであれば、 実行キューにはタスク G がないため、P は他の P から G を取得して実行する必要があります。一般に、P が他の P からタスクを取得したい場合、通常は run がかかります キューの半分。これにより、以下に示すように、各 OS スレッドが完全に使用できるようになります。

3. GPM 作成に関する問題

M と P の数を決定するにはどうすればよいですか?あるいは、M と P はいつ作成されますか?

1. P:

  • の数は、起動時の環境変数 $GOMAXPROCS またはランタイム メソッド GOMAXPROCS() によって決まります (デフォルトは 1)。これは、プログラム実行中のいかなる時点でも、$GOMAXPROCS ゴルーチンのみが同時に実行されることを意味します。

2. M の数:

  • go 言語自体の制限: go プログラムの開始時に、M の最大数が設定され、デフォルトは です。ただし、カーネルがこれをサポートすることは困難であり、非常に多くのスレッドがあるため、この制限は無視できます。
  • 実行時/デバッグの SetMaxThreads 関数は、M の最大数を設定します。
  • M がブロックされた場合、新しい M が作成されます。

M は P の数と絶対的な関係はありません。1 つの M がブロックすると、P は別の M を作成するか、別の M に切り替えます。したがって、P のデフォルトの数が 1 であっても、多くの M がブロックされる可能性があります。作成されました。

3. P の作成時: P の最大数 n を決定した後、ランタイム システムはこの数に基づいて n 個の P を作成します。

4. M が作成されるとき: P に関連付けてその中で実行可能な G を実行するのに十分な M がありません。たとえば、この時点ですべての M がブロックされており、P にまだ多くの準備完了タスクがある場合、アイドル状態の M が検索されます。アイドル状態の M がない場合は、新しい M が作成されます。

M はどの P 協会を選択する必要がありますか?

  • M は、この M が作成される原因となった P アソシエーションを選択します。

PとMの関係はいつ入れ替わるのでしょうか?

システムコールによりMがブロックされると(M上で動作するGがシステムコールに入る)、MとPが分離されますが、このときPのレディキューにタスクがあれば、
Pそれアイドル状態の M に関連付けられるか、関連付け用の M が作成されます。 (つまり、go は libtask のような IO ブロックを処理しませんか? よくわかりません。)

ready G はどの P の Ready キューに入るかをどのように選択するのでしょうか?

  • デフォルト: P のデフォルト数は 1 であるため (M は必ずしも 1 であるとは限りません)、GOMAXPROCS を変更しない場合、プログラム内で go ステートメントを使用してゴルーチンをいくつ作成しても、同じ P の準備完了キューにのみ詰め込まれます。
  • 複数の P がある場合: GOMAXPROCS が変更されるか runtime.GOMAXPROCS が呼び出される場合、ランタイム システムは各 P のレディ キュー内のすべての G を均等に分配します。

各 P のレディ キューに G があることを確認する方法

P のレディ キュー内のすべてのタスクが実行された場合、P は他の P のレディ キューからタスクを取り出そうとします。その一部は、各 P のレディ キューに実行するタスクがあることを保証するために、独自のレディ キューに入れられます。

以上がGolang コルーチンのスケジューリングについての詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:cnblogs.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!