channel
Go 言語で goroutine 間のデータ通信と同期を行う型付きパイプ
Go並行処理
channel とは
channel は Go 言語の組み込み型で、goroutine 間でデータを安全に送受信する型付きパイプである。共有メモリではなく、メッセージパッシングで goroutine 間の通信を行う。Go の並行処理の哲学「メモリを共有して通信するな、通信してメモリを共有せよ」を体現する。
基本的な使い方
// チャネルの作成
ch := make(chan string)
// goroutine で送信
go func() {
ch <- "Hello" // 送信 (受信されるまでブロック)
}()
// メインで受信
msg := <-ch // 受信 (送信されるまでブロック)
fmt.Println(msg) // "Hello"
バッファなし vs バッファ付き
// バッファなし (同期): 送信と受信が同時に行われる
ch := make(chan int)
// 送信側は受信側が準備できるまでブロック
// バッファ付き (非同期): バッファが満杯になるまで送信がブロックしない
ch := make(chan int, 10)
// 10 個までバッファに溜められる
| 種類 | 動作 | 用途 |
|---|---|---|
| バッファなし | 送受信が同期 | goroutine 間の同期ポイント |
| バッファ付き | バッファが満杯になるまで非同期 | プロデューサー/コンシューマー |
select 文
複数のチャネルを同時に待ち受ける。最初に準備できたチャネルの操作を実行する。
select {
case msg := <-msgCh:
fmt.Println("Message:", msg)
case err := <-errCh:
fmt.Println("Error:", err)
case <-time.After(5 * time.Second):
fmt.Println("Timeout")
}
select は Go の並行処理で最も強力な構文だ。タイムアウト、キャンセル、複数ソースからの受信を簡潔に記述できる。
チャネルの方向
func producer(out chan<- int) { out <- 42 } // 送信専用
func consumer(in <-chan int) { v := <-in } // 受信専用
チャネルの方向を制限することで、誤った操作をコンパイル時に防ぐ。
チャネルのクローズ
close(ch) // チャネルを閉じる (これ以上送信しない)
// range でチャネルが閉じるまで受信
for msg := range ch {
fmt.Println(msg)
}
TypeScript の類似概念
TypeScript には channel がないが、AsyncGenerator や ReadableStream が類似の概念を提供する。
Go の並行処理パターン比較
| パターン | 説明 | 用途 |
|---|---|---|
| チャネル | ゴルーチン間のメッセージパッシング | データの受け渡し |
| sync.Mutex | 排他制御 | 共有メモリの保護 |
| sync.WaitGroup | ゴルーチンの完了待ち | 並列処理の同期 |
| select | 複数チャネルの待ち受け | タイムアウト、多重化 |
| context | キャンセル、タイムアウト | リクエストのライフサイクル |
さらに掘り下げるなら関連書籍が参考になる。