はい、それは複雑ですが、物事をはるかに簡単に感じさせるべき経験則がいくつかあります。
- グローバルスコープのチャネルにアクセスするのではなく、go-routineに渡すチャネルに正式な引数を使用することをお勧めします。この方法でより多くのコンパイラチェックを取得でき、モジュール性も向上します。
- 特定のゴールーチン(「メイン」チャネルを含む)で同じチャネルで読み取りと書き込みの両方を行うことは避けてください。そうしないと、デッドロックがはるかに大きなリスクになります。
これらの2つのガイドラインを適用した、プログラムの代替バージョンを次に示します。このケースは、チャネル上の多くのライターと1人のリーダーを示しています。
c := make(chan string)
for i := 1; i <= 5; i++ {
go func(i int, co chan<- string) {
for j := 1; j <= 5; j++ {
co <- fmt.Sprintf("hi from %d.%d", i, j)
}
}(i, c)
}
for i := 1; i <= 25; i++ {
fmt.Println(<-c)
}
http://play.golang.org/p/quQn7xePLw
これは、1つのチャネルに書き込む5つのゴールーチンを作成し、それぞれが5回書き込みます。メインのgo-routineは、25個のメッセージすべてを読み取ります。メッセージが表示される順序が連続していないことがよくあります(つまり、同時実行性が明らかです)。
この例は、Goチャネルの機能を示しています。複数のライターが1つのチャネルを共有することが可能です。Goはメッセージを自動的にインターリーブします。
ここの2番目の例に見られるように、同じことが1つのチャネル上の1つのライターと複数のリーダーに当てはまります。
c := make(chan int)
var w sync.WaitGroup
w.Add(5)
for i := 1; i <= 5; i++ {
go func(i int, ci <-chan int) {
j := 1
for v := range ci {
time.Sleep(time.Millisecond)
fmt.Printf("%d.%d got %d\n", i, j, v)
j += 1
}
w.Done()
}(i, c)
}
for i := 1; i <= 25; i++ {
c <- i
}
close(c)
w.Wait()
この2番目の例には、メインのゴルーチンに課せられた待機が含まれています。待機がないとすぐに終了し、他の5つのゴルーチンが早期に終了します(この修正についてはolovに感謝します)。
どちらの例でも、バッファリングは必要ありませんでした。一般に、バッファリングをパフォーマンス向上剤としてのみ見なすのは良い原則です。あなたのプログラムがデッドロックしない場合はなしバッファ、それがデッドロックではないだろうとのいずれかのバッファ(その逆はできません常に真)。したがって、別の経験則として、バッファリングせずに開始し、必要に応じて後で追加します。
original, hi from 4
...