ノート:
Go 1.5 の時点で、GOMAXPROCS はハードウェアのコア数に設定されています: golang.org/doc/go1.5#runtime、1.5 より前の元の回答の下。
GOMAXPROCS 環境変数を指定せずに Go プログラムを実行すると、Go ゴルーチンは単一の OS スレッドで実行されるようにスケジュールされます。ただし、プログラムがマルチスレッド化されているように見せるために (それがゴルーチンの目的ですよね?)、Go スケジューラは時々実行コンテキストを切り替える必要があるため、各ゴルーチンはその作業を行うことができます。
前述したように、GOMAXPROCS 変数が指定されていない場合、Go ランタイムは 1 つのスレッドしか使用できないため、ゴルーチンが計算や IO (プレーンな C 関数にマップされる) などの従来の作業を実行している間は、実行コンテキストを切り替えることはできません。 )。コンテキストを切り替えることができるのは、Go 同時実行プリミティブが使用されている場合、たとえば、複数のチャンをオンにする場合、または (これはあなたの場合です) スケジューラにコンテキストを切り替えるように明示的に指示する場合です。これruntime.Gosched
が目的です。
つまり、あるゴルーチンの実行コンテキストがGosched
呼び出しに到達すると、スケジューラは実行を別のゴルーチンに切り替えるように指示されます。あなたの場合、メイン(プログラムの「メイン」スレッドを表す)と追加の2つのゴルーチンがありますgo say
。呼び出しを削除するGosched
と、実行コンテキストが最初の goroutine から 2 番目の goroutine に転送されることはないため、「世界」はありません。が存在する場合Gosched
、スケジューラは各ループ反復の実行を最初の goroutine から 2 番目の goroutine に、またはその逆に転送するため、'hello' と 'world' がインターリーブされます。
参考までに、これは「協調マルチタスキング」と呼ばれます。ゴルーチンは明示的に制御を他のゴルーチンに譲らなければなりません。現在のほとんどの OS で使用されているアプローチは、「プリエンプティブ マルチタスキング」と呼ばれます。実行スレッドは、制御の転送には関与しません。代わりに、スケジューラが透過的に実行コンテキストを切り替えます。協調的アプローチは、「グリーン スレッド」、つまり OS スレッドに 1:1 でマップされない論理的な同時実行コルーチンを実装するためによく使用されます。これが、Go ランタイムとそのゴルーチンが実装される方法です。
アップデート
GOMAXPROCS 環境変数について言及しましたが、それが何であるかは説明しませんでした。これを修正する時が来ました。
この変数が正の数に設定されている場合、Go ランタイムは、すべてのグリーン スレッドがスケジュールされるネイティブ スレッドN
まで作成できます。N
ネイティブ スレッド オペレーティング システム (Windows スレッド、pthread など) によって作成される一種のスレッド。これは、N
が 1 より大きい場合、ゴルーチンが異なるネイティブ スレッドで実行されるようにスケジュールされ、その結果、並列で実行される可能性があることを意味します (少なくとも、コンピューターの能力まで: システムがマルチコア プロセッサに基づいている場合、これらのスレッドは真に並列になる可能性が高く、プロセッサがシングル コアの場合、OS スレッドにプリエンプティブ マルチタスキングが実装され、並列実行が可視化されます)。
runtime.GOMAXPROCS()
環境変数を事前に設定する代わりに、関数を使用して GOMAXPROCS 変数を設定することができます。現在の の代わりに、プログラムで次のようなものを使用しますmain
。
func main() {
runtime.GOMAXPROCS(2)
go say("world")
say("hello")
}
この場合、興味深い結果が観察できます。'hello' と 'world' の行が不均等に交互に印刷される可能性があります。
hello
hello
world
hello
world
world
...
これは、ゴルーチンが OS スレッドを分離するようにスケジュールされている場合に発生する可能性があります。これは実際、プリエンプティブ マルチタスク (またはマルチコア システムの場合は並列処理) がどのように機能するかです。スレッドは並列であり、結合された出力は不確定です。ところで、通話を終了または削除できますがGosched
、GOMAXPROCS が 1 より大きい場合は効果がないようです。
runtime.GOMAXPROCS
以下は、 callを使用してプログラムを数回実行した結果です。
hyperplex /tmp % go run test.go
hello
hello
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
hello
hello
hello
hello
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
ほら、出力がきれいな場合もあれば、そうでない場合もあります。行動中の不確定性:)
別のアップデート
Go コンパイラの新しいバージョンでは、Go ランタイムが同時実行プリミティブの使用だけでなく、OS システム コールでも goroutine を強制的に生成するように見えます。これは、IO 関数の呼び出しでも実行コンテキストをゴルーチン間で切り替えることができることを意味します。その結果、最近の Go コンパイラでは、GOMAXPROCS が設定されていない場合や 1 に設定されている場合でも、不確定な動作を観察することができます。