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 的三個差別
switch | select | |
|---|---|---|
| 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 後面不是條件而是一個 channel、select 分三階段:求值全部、等待全部、只執行一個