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 キャンセル、タイムアウト リクエストのライフサイクル

さらに掘り下げるなら関連書籍が参考になる。

関連用語