select 阻塞的是呼叫它的 goroutine,不是整個程式
for裡的 select 確實是同步的:這一圈選到並執行完,才會進下一圈。但要分清楚——「main 在等」不等於「整個程式在等」。
go func() { time.Sleep(1 * time.Second); a <- "A" }() // 背景同時在跑
go func() { time.Sleep(2 * time.Second); b <- "B" }()
for i := 0; i < 2; i++ {
select {
case msg := <-a: fmt.Println(msg)
case msg := <-b: fmt.Println(msg)
}
}時間軸 0s -------- 1s -------- 2s
main: [ select 等... ] [ select 等... ]
goroutine A: [ 睡 1 秒 ] → 丟資料
goroutine B: [ 睡 2 秒 ............] → 丟資料A 睡 1 秒 + B 睡 2 秒,總共只花 2 秒不是 3 秒。 兩條 goroutine 從頭到尾都在同時推進,被卡住的只有 main 這一條。
所以 select 的定位是:讓一條執行流去「收割」多條平行工作的結果。它本身是同步的(會卡住呼叫它的那條 goroutine),但它等待的對象是非同步的。
這也是為什麼 select 幾乎總是寫在 for 裡——一次 select 只處理一個事件,要持續收割就得繞回來再等一次。
相關:Goroutine 是用 go 關鍵字啟動的輕量任務、select 等不到條件時會 deadlock 或 goroutine leak