select 分三階段:求值全部、等待全部、只執行一個

「select 會把所有 case 都跑一遍嗎?」——對一半。要拆成三個階段看:

階段做什麼涵蓋哪些 case
① 求值算出每個 case 要等的 channel 是誰全部,由上到下各算一次
② 等待同時盯著這些 channel全部
③ 執行跑 case 大括號裡的程式碼只有贏的那一個
func makeChan(name string, delay time.Duration) <-chan string {
	fmt.Println("  求值階段:呼叫了 makeChan(" + name + ")")
	ch := make(chan string)
	go func() { time.Sleep(delay); ch <- name }()
	return ch
}
 
select {
case msg := <-makeChan("A", 3*time.Second): fmt.Println("執行階段:A", msg)
case msg := <-makeChan("B", 1*time.Second): fmt.Println("執行階段:B", msg)
case msg := <-makeChan("C", 2*time.Second): fmt.Println("執行階段:C", msg)
}
  求值階段:呼叫了 makeChan(A)
  求值階段:呼叫了 makeChan(B)
  求值階段:呼叫了 makeChan(C)
執行階段:B B

三個 makeChan 都被呼叫了(含它們各自開的 goroutine),但只有最快的 B 的 body 被執行。A 與 C 的 goroutine 仍在背景睡,時間到會送資料卻沒人接,那些資料就浪費掉了。

副作用是實際的:case 的 channel 運算式若有副作用(開連線、發請求、起 goroutine),每次跑到這個 select 都會全部發生一次,不是只發生在被選中的那條。

用詞修正

channel 不是「回傳」結果,而是**「有沒有資料可以拿」**。select 等的是「可讀 / 可寫」這件事,不是函式回傳值。

相關:select 的 case 後面不是條件而是一個 channelselect 是 channel 版的 switch