web-dev-qa-db-ja.com

特定のゴルーチンを実行するのにtime.sleepが必要なのはなぜですか?

GOチュートリアルには、次のスライドがあります。 Goroutines

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

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

このコードを実行すると、期待どおりの結果が得られます(「world」と「hello」が画面に交互に5回書き込まれます)。

ただし、time.Sleep(したがって、インポートの"time"行)をコメントアウトしてプログラムを再度実行すると、only画面に5回「こんにちは」と書かれました。

ゴルーチンが死ぬのを防ぐtime.Sleepの何がそれほど重要ですか?

29
Darrrrrren

_time.Sleep_を削除すると、say("world")ゴルーチンに実行する機会が与えられません。 goroutineスケジューラーはプリエンプティブではありません。別のゴルーチンが実行される前に、ゴルーチンは制御を放棄する必要があります。コントロールを放棄する1つの方法は、_time.Sleep_を実行することです。

say関数から_time.Sleep_を取り出すと、プライマリゴルーチンが5回実行され、セカンダリゴルーチンへの制御を放棄することなく、プライマリゴルーチンがsayから戻ったときに、プログラムプログラムを存続させるものがないため、終了します。

34
Daniel

Goroutineスケジューラはプリエンプティブではないため、別のgoroutineが実行される前に、ゴルーチンが制御を放棄する必要があります。コントロールを放棄する1つの方法は、_time.Sleep_を使用することです。もう1つの方法は、runtime.Gosched()を使用することです。

Gosched()を使用するように変更されたチュートリアルは次のとおりです。 http://play.golang.org/p/jQ9mlGYXXE

これは、ゴルーチンを理解する上で役立つレッスンです。ただし、スケジューラーを直接制御することは、間違いなくアンチパターンです。悲しみはしばしば続きます。

代わりに、通信するデジタルハードウェアのチャンクのようなゴルーチンについて考えてみてください(ステートマシンは良いアナロジーです)。 goroutinesのベースとなっている Communicating Sequential Processes モデルについて学習することをお勧めします。 CSPベースの設計では、各ゴルーチンは独自のプライベート状態を持ち、メッセージを交換して他のゴルーチンの状態と対話します。メッセージの受け渡しにより同期が強制され、スケジューラーはこれを使用して、CPU時間を取得するアクティビティと待機キューに配置するアクティビティを決定します。

この方法でGoにアプローチする場合、おそらくスケジューラの内部について心配する必要はありません。

9
Rick-777

time.Sleepをsay関数から削除すると、メインはsay( "hello")を実行し、ゴルーチンを実行せずに終了します。 time.Sleep(またはそれ以外の場合はselect {})をメインエンドの前に追加すると、goroutineに実行する時間を与え、そのスレッドはスケジューラから選択されます。

例:

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        // time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

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

    time.Sleep(1*time.Second)
    // Vs: 
    // select {} // blocks indefinitely, requires manual interrupt
          // In CSP-speak the empty select is like STOP.
          // for{} would cause the cpu to max and the process's STATE will be `running`
          // select{} will not cause the cpu to max and the process state will be `sleeping`
}

出力は通常5helloの後に5worldが続きますが、最後のhelloの前にworldの1つを印刷することもできます

TRY IT->(http://)goo.gl/K2v7H0

1
Ga Sacchi