select 等不到條件時會 deadlock 或 goroutine leak

沒有 default、又沒有任何 channel 會來資料,select 就永遠卡住。結局有兩種,而且吵的那種其實是在幫你

情況一:所有 goroutine 都在等 → panic

ch := make(chan string)   // 沒有任何人會往這裡送資料
select {
case msg := <-ch:
    fmt.Println(msg)
}
開始等...
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
exit status 2

Go runtime 發現沒有任何一條 goroutine 還能推進,直接 panic 結束程式。問題當場暴露,算是最好的結果。

情況二:還有別的 goroutine 活著 → goroutine leak

只要程式裡還有其他能跑的 goroutine(例如一個 HTTP server),runtime 就不會判定 deadlock。那條卡住的 goroutine 會永遠留著、佔記憶體不放,這叫 goroutine leak

  • 沒有錯誤訊息、沒有 panic
  • 程式看起來活得好好的,只是某個功能沒反應
  • 累積久了記憶體一路長

這是線上服務最難查的 bug 之一,也是為什麼寫 select 時要主動決定「等不到怎麼辦」,見 select 用 default 做非阻塞,用 time.After 做逾時

順帶一提,空的 select{} 會永久阻塞——偶爾被拿來當「讓 main 永遠不結束」的寫法。

相關:無緩衝 channel 的收送會互相等待select 阻塞的是呼叫它的 goroutine,不是整個程式