select 用 default 做非阻塞,用 time.After 做逾時
寫 select 時要主動決定「等不到怎麼辦」。三種選擇:
| 寫法 | 行為 | 什麼時候用 |
|---|---|---|
| 什麼都不加 | 等到天荒地老 | 你確定一定會有資料來 |
default | 完全不等,立刻往下 | 輪詢、非阻塞檢查 |
case <-time.After(N) | 最多等 N 秒 | 最常用:呼叫外部服務、等 DB 回應 |
// 解法 1:default —— 沒資料就不等
select {
case msg := <-ch:
fmt.Println("收到:", msg)
default:
fmt.Println("現在沒資料,我不等了,繼續做別的事")
}
// 解法 2:time.After —— 給一個等待上限
select {
case msg := <-ch:
fmt.Println("收到:", msg)
case <-time.After(2 * time.Second):
fmt.Println("等了 2 秒還是沒來,放棄")
}default 怎麼判斷要不要跳進去
select 執行的那一瞬間做一次檢查:掃過所有 case,看有沒有哪個 channel「現在立刻」有資料 → 有就執行(多個都有則隨機挑)→ 都沒有就看有沒有 default,有就執行 default 馬上往下走,沒有就停在這裡等。
這個檢查不會等待。有 default 就等於「非阻塞 select」。
⚠️ default 與 timeout 不要同時寫
select {
case msg := <-ch:
case <-time.After(2 * time.Second): // 永遠輪不到
default: // 每次都走這裡
}有 default 的話 select 根本不會等,那個 timeout case 沒有任何機會就緒。兩者是互斥的設計意圖:一個是「完全不等」,一個是「等但有上限」。
相關:select 等不到條件時會 deadlock 或 goroutine leak、for-select 裡的 time.After 每圈重置