[解決済み] 複数のゴルーチンが1つのチャンネルをリッスンする
質問
複数のゴルーチンが同じチャネルで同時に受信を試みています。チャネルで受信を開始した最後のゴルーチンが値を取得するようです。これは言語仕様のどこかにあるのでしょうか、それとも未定義の動作なのでしょうか。
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
<-c
c <- fmt.Sprintf("goroutine %d", i)
}(i)
}
c <- "hi"
fmt.Println(<-c)
出力します。
goroutine 4
EDITです。
思ったより複雑なことに気づきました。メッセージはすべてのゴルーチンを回って渡されます。
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
msg := <-c
c <- fmt.Sprintf("%s, hi from %d", msg, i)
}(i)
}
c <- "original"
fmt.Println(<-c)
出力します。
original, hi from 0, hi from 1, hi from 2, hi from 3, hi from 4
注意 上記の出力は最近のバージョンのGoでは古くなっています(コメントを参照)
どのように解決するのですか?
確かに複雑ですが、いくつかの経験則があるので、物事はもっと簡単に感じられるはずです。
- チャンネルに正式な引数を使用することを好む に正式な引数を使うことを好みます。この方法でより多くのコンパイラチェックを受けることができますし、より良いモジュール性も得られます。
- 特定のゴー・ルーチンで同じチャンネルの読み書きの両方を避けることができます。 (「メイン」ルーチンを含む)。さもなければ、デッドロックがより大きなリスクとなります。
この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
これは単一のチャネルに書き込む 5 つのゴー ルーチンを作成し、それぞれが 5 回書き込んでいます。メインゴールーチンは25のメッセージをすべて読み込みますが、その順番がしばしば順不同であることに気づくかもしれません(つまり、並行処理が明白であることです)。
この例は、Goチャンネルの特徴を示しています。複数のライターが1つのチャンネルを共有することが可能で、Goは自動的にメッセージをインターリーブします。
同じことが、1つのチャネルに1人のライターと複数のリーダーがいる場合にも当てはまります(2番目の例)。
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()
これは 第二例 には、メインゴルーチンに課せられた待ちが含まれています。この待ちがなければ、メインゴルーチンは速やかに終了し、他の5つのゴルーチンを早期に終了させることになります。 (これは olov に感謝します) .
どちらの例でも、バッファリングは必要ありませんでした。一般的に、バッファリングはパフォーマンス向上のためだけのものと考えるのが良い原則です。プログラムがデッドロックしないのであれば なしで バッファがなければデッドロックは起きません。 で バッファでもデッドロックは起こりません (しかし、その逆で は は常に真である)。そのため もう一つの経験則として、バッファリングなしで開始し、必要に応じて後で追加してください。 .
関連
-
[解決済み] Goでスライスを逆に反復処理する方法はありますか?
-
[解決済み] Goプロジェクトの賢明なレイアウト方法とは [終了しました]。
-
[解決済み] golangでは、マップから値のスライスを取得する良い方法はありますか?
-
[解決済み] テキストファイルを文字列配列に読み込む(そして書き込む)
-
[解決済み] テンプレートでマップを繰り返し処理する
-
[解決済み] あるプログラムの依存ファイルをすべて取得する方法
-
[解決済み] 構造体の値をマップの値として設定すると、"cannot assign" エラーが発生するのはなぜですか?重複
-
[解決済み] bytes.Buffer does not implement io.Writer" というエラーメッセージが表示される。
-
[解決済み] チャネルの要素数
-
[解決済み] Goでマップをクリアするには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Goを使って大きなファイルを効率よくダウンロードするにはどうしたらいいですか?
-
[解決済み] reflectを使用して、構造体フィールドの値を設定するにはどうすればよいですか?
-
[解決済み] os.Exit()とpanic()はいつ使うのか?
-
[解決済み] 現在時刻に時・分・秒を足す
-
[解決済み] あるプログラムの依存ファイルをすべて取得する方法
-
[解決済み] Go で子プロセスの標準出力パイプをリダイレクトする
-
[解決済み] Goのコンパイル済み実行ファイルのサイズが巨大化する理由
-
[解決済み] チャネルの要素数
-
[解決済み] ローカルファイルからio.Readerを作成する
-
[解決済み] IntelliJでファイル保存時にgofmtを実行する