💪
【Go 腕試し】複数チャネルのselect
What's this?
Go の入門者から上級者まで幅広く多くの Gopher が Go を楽しみつくすためのクイズを作りました。
ぜひチャレンジしてみてください!
さっそく問題!
package main
import "fmt"
func main() {
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
ch1 <- 1
ch2 <- 2
select {
case v := <-ch1:
fmt.Println(v)
case v := <-ch2:
fmt.Println(v)
}
}
-
1が出力される -
2が出力される -
1か2がランダムに出力される - デッドロックが発生する
※環境依存がない前提での出題ですが、何かあればごめんなさい。。。
※Go 1.25 で動作確認をしています。
回答・解説
答えはこちら
正解は 3 の 1 または 2 がランダムに出力されるです!
正解できましたかね?
簡単だぜ!ってかたはごめんなさい。次こそリベンジします。
解説はこんな感じです。
package main
import "fmt"
func main() {
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
ch1 <- 1
ch2 <- 2
// select文に到達した時点で、両方のcaseが準備完了している
select {
case v := <-ch1:
fmt.Println(v) // ch1から受信可能
case v := <-ch2:
fmt.Println(v) // ch2からも受信可能
}
// Goの仕様:複数のcaseが準備完了している場合は疑似ランダムに1つを選択
// そのため、実行ごとに 1 が出力されたり 2 が出力されたりする
}
ポイント:
多くの人は「select は上から順に評価される」と思ってコードを読んだと思います。ただし、実際には複数の case が準備完了している場合、Go は疑似ランダムに 1 つを選択します。
これは公平性(fairness)を保つための意図的な設計です。もし上から順に評価されると、以下のようなループで特定のチャネルが永遠に無視される可能性があります:
// もし上から順なら、ch1に常にデータがある場合、ch2は永遠に選ばれない
for {
select {
case v := <-ch1: // 常に優先
process(v)
case v := <-ch2: // 無視される
process(v)
}
}
そのため、Go はランダム選択を採用しています。
実行してみると:
$ go run main.go
1
$ go run main.go
2
$ go run main.go
1
同じコードでも実行ごとに結果が変わることが確認できるはずです。
細かい仕様ですが、へ~と思ってくださればうれしいです。
References
-
Go 公式 Doc | Select statements
"If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection."
- Effective Go | Channels
- Go by Example: Select
- The Go Programming Language Specification | Select statements
さいごに
どうでしたか?
Gopher のみなさん、楽しんでもらえましたかね?
今回余裕の正解だった上級者の方はごめんなさい。次こそリベンジします!
今後も Go の勉強になるようなクイズを作りますので、次回のチャレンジもお待ちしております!
Discussion