注意:
Go 1.5の時点で、GOMAXPROCSはハードウェアのコア数golang.org/doc/go1.5#runtimeに設定されており、1.5より前の元の回答よりも少なくなっています。
GOMAXPROCS環境変数を指定せずにGoプログラムを実行すると、Goゴルーチンは単一のOSスレッドで実行されるようにスケジュールされます。ただし、プログラムをマルチスレッドのように見せるために(これがゴルーチンの目的ですよね?)、Goスケジューラーは実行コンテキストを切り替える必要がある場合があります。これにより、各ゴルーチンがその作業を実行できるようになります。
すでに述べたように、GOMAXPROCS変数が指定されていない場合、Goランタイムは1つのスレッドしか使用できないため、goroutineが計算やIO(プレーンC関数にマップされている)などの従来の作業を実行している間は、実行コンテキストを切り替えることはできません。 )。コンテキストは、Go同時実行プリミティブが使用されている場合にのみ切り替えることができます。たとえば、複数のchanをオンにした場合、または(これはあなたの場合です)スケジューラーにコンテキストを切り替えるように明示的に指示した場合ですruntime.Gosched
。これが目的です。
つまり、あるゴルーチンの実行コンテキストがGosched
呼び出しに達すると、スケジューラーは実行を別のゴルーチンに切り替えるように指示されます。あなたの場合、main(プログラムの「main」スレッドを表す)と追加の2つのgoroutineがあり、1つはで作成したものですgo say
。Gosched
呼び出しを削除すると、実行コンテキストが最初のゴルーチンから2番目のゴルーチンに転送されることはないため、「ワールド」はありません。ときGosched
スケジューラ転送が、その逆秒に最初のゴルーチンから各ループ反復の実行と悪徳、存在しているあなたが持っているので、「こんにちは」と「世界は」インターリーブ。
参考までに、これは「協調マルチタスク」と呼ばれます。ゴルーチンは、他のゴルーチンに制御を明示的に譲る必要があります。最新のOSで使用されているアプローチは、「プリエンプティブマルチタスク」と呼ばれます。実行スレッドは制御の転送に関係しません。スケジューラーは、代わりに実行コンテキストを透過的に切り替えます。協調的アプローチは、「グリーンスレッド」、つまりOSスレッドに1:1でマップされない論理同時コルーチンを実装するために頻繁に使用されます。これがGoランタイムとそのゴルーチンの実装方法です。
更新
GOMAXPROCS環境変数について説明しましたが、それが何であるかについては説明しませんでした。これを修正する時が来ました。
この変数が正の数に設定されている場合N
、Goランタイムは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
呼び出しを使ってプログラムを数回実行したときに得たものです。
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システムコールでもゴルーチンを強制的に生成するように見えます。これは、IO関数呼び出しでも実行コンテキストをゴルーチン間で切り替えることができることを意味します。その結果、最近のGoコンパイラでは、GOMAXPROCSが設定されていないか1に設定されている場合でも、不確定な動作を観察することができます。