runtime.Goschedは正確に何をしますか?


86

ツアーゴーのウェブサイトの行く1.5のリリースに先立って、バージョン、コードの一部があることを次のようになります。

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say("world")
    say("hello")
}

出力は次のようになります。

hello
world
hello
world
hello
world
hello
world
hello

私を悩ませているのは、runtime.Gosched()削除されると、プログラムが「ワールド」を出力しなくなることです。

hello
hello
hello
hello
hello

どうしてこんなことに?runtime.Gosched()実行にどのように影響しますか?

回答:


143

注意:

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 sayGosched呼び出しを削除すると、実行コンテキストが最初のゴルーチンから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に設定されている場合でも、不確定な動作を観察することができます。


よくやった !しかし、私はgo 1.0.3、wierdの下でこの問題に遭遇しませんでした。
woooHaaaa 2012年

1
これは本当です。go 1.0.3でこれを確認したところ、この動作は表示されませんでした。GOMAXPROCS== 1の場合でも、プログラムはGOMAXPROCS> = 2のように動作しました。1.0.3ではスケジューラーが調整されているようです。
ウラジミールマトベーエフ2012年

1.4コンパイラーで状況が変わったと思います。OPの質問の例は、OSスレッドを作成しているようですが、これ(-> gobyexample.com/atomic-counters)は協調スケジューリングを作成しているようです。これが当てはまる場合は、回答を更新してください
tez 2015年

8
Go 1.5の時点で、GOMAXPROCSはハードウェアのコア数に設定されています:golang.org/doc/go1.5#runtime
thepanuto

1
@paulkon、Gosched()必要かどうかはプログラムによって異なりますが、GOMAXPROCS値には依存しません。協調的なマルチタスクよりも先制的なマルチタスクの効率も、プログラムによって異なります。プログラムがI / Oバウンドの場合、非同期I / Oを使用した協調マルチタスクは、同期スレッドベースのI / Oよりもおそらく効率的です(つまり、スループットが高くなります)。プログラムがCPUに依存している場合(たとえば、長い計算)、協調マルチタスクはあまり役に立ちません。
ウラジミールマトベーエフ2015年

8

協調型スケジューリングが原因です。譲歩しないと、他の(「世界」と言う)ゴルーチンは、メインが終了する前/終了するときに合法的に実行される機会がゼロになる可能性があります。つまり、仕様に従ってすべてのゴルーチンが終了します。プロセス全体。


1
大丈夫、そうruntime.Gosched()収量。どういう意味ですか?それは制御を主な機能に戻しますか?
Jason Yeo

5
この特定のケースでは、はい。通常、スケジューラーは、意図的に指定されていない選択順序で「準備完了」ゴルーチンのいずれかを開始して実行するように求められます。
zzzz 2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.