Goにおいて、チャネルはゴルーチン間でデータを同期して転送する手段です。次の2種類があります:
非バッファリングのチャネルは、同期が重要で、送信者と受信者がコードの同じ場所で出会うことを保証する必要がある場合に便利です。バッファリングされたチャネルは、「余分な」データを転送するために適しており、キュー、ワーカー、非同期タスクの管理に使用されます。
func main() { ch := make(chan int) // 非バッファリング go func() { ch <- 42 // 受信者がいないとブロックされる }() fmt.Println(<-ch) bufch := make(chan int, 2) // 2要素のバッファリングチャネル bufch <- 1 // ブロックされない bufch <- 2 // ブロックされない // 次のsendは読み取りがなければブロックされる }
バッファリングされたチャネルは、複数のプロデューサーとコンシューマー間のキューの代替として、追加の同期なしに機能することができますか?
多くの場合、「はい」と答えられますが、実際には順序の保証が重要でなく、メッセージが失われない場合のみです。複数のリーダーやライターがいる場合、競合やデータ損失の可能性があり、同期の制御は依然として必要です(ワーカープール、ミューテックス、チャネルを閉じるためのコンテキスト)。
物語
ログ処理では、プログラマーはパーサーとアグリゲーター間でイベントを送るために、長さ1000のバッファリングチャネルを使用しました。サービス停止時にチャネルを閉じるのを忘れ、いくつかのイベントが「失われ」ました—ワーカーはバッファ全体を処理する前に完了しました。明示的にチャネルを閉じ、完全な出力が得られるまでブロックすることで修正されました。
物語
分散サービスでミューテックスキューをバッファリングチャネルに置き換えようとした際、「停止」している送信者の状況を考慮しませんでした:バッファが満杯のときにsendがブロックされ、最終的にシステム全体が遅くなり、タイムアウトが発生し、インターフェースが「ラグる」ようになりました。分析後、条件変数を通じて一部の同期を元に戻しました。
物語
テレメトリモジュールでは、モジュールとコンシューマ間のエラーログ用に非バッファリングチャネルが使用されていました。処理の遅延によりライターがブロックされ、1分間で数十万件のデータがゴルーチン内に蓄積され、「out of goroutines」が発生しました。非バッファリングチャネルをバッファリングチャネルに置き換え、キューをクリアする非同期ワーカーを追加することで修正されました。