select 是 channel 版的 switch

select 同時盯著好幾根 channel,誰先有資料就處理誰。只有一個 channel 時不需要它(直接 msg := <-ch 就好),它是為了「同時等多個」才存在。

a := make(chan string)
b := make(chan string)
 
go func() { time.Sleep(1 * time.Second); a <- "我是 A,我等了 1 秒" }()
go func() { time.Sleep(2 * time.Second); b <- "我是 B,我等了 2 秒" }()
 
for i := 0; i < 2; i++ {
    select {
    case msg := <-a:
        fmt.Println("收到:", msg)
    case msg := <-b:
        fmt.Println("收到:", msg)
    }
}
收到: 我是 A,我等了 1 秒
收到: 我是 B,我等了 2 秒

跟 switch 的三個差別

switchselect
case 比什麼必須是 channel 的收 <-ch 或送 ch <- x
誰先被選中由上往下比,第一個符合的誰先就緒誰贏,跟寫的順序無關
多個同時符合取最上面那個隨機挑一個

「隨機挑」不是實作細節而是語言規格,用意是避免上面的 case 餓死下面的。

case 的三種寫法

case msg := <-ch:      // 拿出來存進 msg,要用它
case <-ch:             // 拿出來但不要值,只在乎「有資料了」
case msg, ok := <-ch:  // 多拿一個 ok:channel 還開著嗎(false = 已被 close)

msg := <-ch 就是一般的賦值,跟 x := 10 同一種東西,只是右邊的值來源是「從 ch 拿一個出來」。

⚠️ msg 只活在它自己那個 case 裡,出了 select 就不存在(編譯錯誤)。所以兩個 case 都能用 msg 這個名字而不衝突——它們是兩個獨立的變數。

相關:Channel 是 goroutine 之間的通訊管道select 的 case 後面不是條件而是一個 channelselect 分三階段:求值全部、等待全部、只執行一個