2016-04-17 23 views
1

私はA Tour of Goから次のコードで遊んでいましたが、マイナーチェンジを適用すると何が起こっているのか分かりません。元のコードは、このゴルーチンを理解しようとしています

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") 
} 

あり、それはOKです。この

world 
hello 
hello 
world 
world 
hello 
hello 
world 
world 
hello 

を生成:5回こんにちは、5回の世界。私は

say("world") 
go say("hello") 

を呼び出すとき、私は今、出力は一切ありませんハローだけ

world 
world 
world 
world 
world 

ある奇妙な取得を開始します。 2つのゴルーチンでさらに奇妙になります

出力がまったくありません。私はi < 2i < 5を変更し、

go say("world") 
say("hello") 

を呼び出すとき、私は私がここで何を

world 
hello 
hello 

をしないのです取得しますか?

+0

https://gobyexample.com/goroutines – elithrar

答えて

5

say("world") 
go say("hello") 

の場合は、「世界」の呼び出しが完了する必要があります。 「こんにちは」のゴルーチンは実行されず、完了しませんbecause main returns

についてゴルーチンは実行されませんか、完全なメイン返すため。

使用sync.WaitGroupが完了ゴルーチンの前に出るのメイン防ぐために:あなたは二つの別々のゴルーチンを作成している

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

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

func main() { 
    var wg sync.WaitGroup 
    wg.Add(2) 
    go say(&wg, "world") 
    go say(&wg, "hello") 
    wg.Wait() 
} 

playground example

1

主な機能が終了したためです。

メイン関数が復帰すると、すべてのゴルーチンが突然終了し、プログラムが終了します。

あなたはなステートメントを追加:メイン関数の戻り値の前に

time.Sleep(100 * time.Second) 

を、すべてがうまくいきます。

しかしGoの良い習慣は、goroutine間の通信に使用されるchannelを使用することです。主な機能がバックグラウンドのゴルーチンの終了を待つようにするために使用することができます。 「こんにちは」ゴルーチンが開始される前に

0

、1が主な機能でありますgoroutineと1つはgo say( "世界")です。通常、関数が実行されると、プログラムはその関数にジャンプし、内部のすべてのコードを実行し、関数が呼び出された後の行にジャンプします。

goroutinesでは、関数内でジャンプしていませんが、別のスレッドでgoroutineを開始し、呼び出し直後にその行を待たずに実行し続けます。

したがって、ゴルーチンは、メインのゴルーチンが完了するまでに終了する時間がありません。

2

おめでとうございます。新しい人物として、並行性とそれが並列性とどのように違うのかを理解することは良いことです。

同時実行
同時実行は片手で空気中のいくつかのボールをジャグリングジャグラーのようなものです。どんなに多くのボールがジャグリングしていても、いつでも1本のボールだけが手を触れる。

並列
ジャグラーが並行して別の手でより多くのボールをジャグリング起動すると、私たちは、同時に実行されている2つの同時プロセスを持っています。彼らは利用できるコンピューティングコアとGOMAXPROCSされている変数のセットに応じて、同時自動並列両方だから

ゴルーチンは素晴らしいです。

片手バック片手、シングル入り、同時ジャグラージャグラーに
。想像してみれば、手がmainルーチンである "hello"、 "world"、 "mars"の3つのボールをそれぞれジャグリングすることを想像してみてください。

var balls = []string{"hello", "world", "mars"} 

func main() { 
     go say(balls[0]) 
     go say(balls[1]) 
     go say(balls[2]) 
} 

以上適切に、

func main() { 
     for _, ball := range balls { 
       go say(ball) 
     } 
} 

3つのボールは空気順次に投げられると、ジャグラーは単純にすぐに手を後退させます。つまり、mainルーチンは、投げられた最初のボールが手に落ちる前に終了します。恥、ボールはちょうど地面に落ちる。悪いショー。

ボールを手に戻すには、ジョグラーが彼にが待っていることを確認する必要があります。これは、彼の手が投げられたボールを追跡してカウントし、それぞれが着陸したときに知ることができる必要があることを意味します。

最も簡単な方法は、sync.WaitGroupを使用することです:

import (
    "fmt" 
    "time" 
    "sync" 
) 

var balls = []string{"hello", "world", "mars"} 
var wg sync.WaitGroup 

func main() { 
     for _, ball := range balls { 
       // One ball thrown 
       wg.Add(1) 
       go func(b string) { 
         // Signals the group that this routine is done. 
         defer wg.Done() 
         // each ball hovers for 1 second 
         time.Sleep(time.Duration(1) * time.Second) 
         fmt.Println(b) 
         // wg.Done() is called before goroutine exits 
       }(ball) 
     } 

     // The juggler can do whatever he pleases while the 
     // balls are hanging in the air. 

     // This hand will come back to grab the balls after 1s. 
     wg.Wait() 
} 

WaitGroupは簡単です。ゴルーチンが生成されると、「バックログカウンタ」にWaitGroup.Add(1)が追加され、WaitGroup.Done()と呼ぶとカウンタが減少します。バックログが0になると、すべてのゴルーチンが完了し、WaitGroupが待機を停止する(そしてボールをつかむ)ことを意味します。

同期にチャネルを使用するのは問題ありませんが、チャネルを使用するとコードが複雑で理解しにくい場合には、特に使用可能な並行ツールを使用することをお勧めします。

関連する問題