select文を用いた並行処理

Goのselect文は、複数のチャネル操作を待ち受けて、どれか一つが準備できた時にその処理を実行する構文です。Goの並行処理において、複数のチャネルからの非同期なメッセージの受信や送信を効率的に扱うために非常に重要な機能です。


基本構文

go
select { case msg1 := <-ch1: // ch1から受信した場合の処理 case ch2 <- msg2: // ch2に送信できた場合の処理 default: // どのチャネルも準備できていない場合の処理(任意) }

特徴と動作

  1. いずれかのcaseが準備できたらその処理を実行

    • 複数のチャネル操作の中で、最初に準備が整ったものが実行されます。

    • 複数のcaseが同時に準備完了していた場合は、ランダムに1つ選ばれる

  2. ブロックされない動作が可能

    • default節を用いることで、どのチャネルも準備できていない場合でも即座に実行できる非ブロッキングな処理が可能です。

  3. 無限ループと組み合わせて使用

    • forループと併用することで、複数のチャネルを監視し続ける並行処理の制御が行えます。


例:複数のチャネルからのメッセージ受信

go
package main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) // 並行に動作するGoroutineを2つ起動 go func() { time.Sleep(2 * time.Second) ch1 <- "message from ch1" }() go func() { time.Sleep(1 * time.Second) ch2 <- "message from ch2" }() // selectでどちらかのチャネルからの受信を待つ select { case msg := <-ch1: fmt.Println("Received:", msg) case msg := <-ch2: fmt.Println("Received:", msg) } }

実行結果(出力):

vbnet
Received: message from ch2

※ 上記の例では、ch2が1秒早く送信するため、それが先に受信されます。


default節を用いた非ブロッキングselectの例

go
select { case msg := <-ch: fmt.Println("Received:", msg) default: fmt.Println("No message received") }

この例では、チャネルchからの受信が可能でない限り、defaultの処理が即座に実行されます。これはタイムアウト処理ポーリングに応用できます。


応用例:タイムアウト処理の実装

go
select { case msg := <-ch: fmt.Println("Received:", msg) case <-time.After(2 * time.Second): fmt.Println("Timeout") }

この例では、2秒以内にチャネルchからメッセージが来なければ、Timeoutメッセージを表示して処理を中断できます。


まとめ

  • select文は複数のチャネル操作を同時に監視できる強力な構文

  • 並行処理の制御、タイムアウト処理、優先度付き処理など、GoのGoroutineとチャネルの活用に不可欠。

  • forループやdefault節と組み合わせることで、より柔軟な並行処理が可能となる。

生成日:2025/05/03