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 2Go runtime 發現沒有任何一條 goroutine 還能推進,直接 panic 結束程式。問題當場暴露,算是最好的結果。
情況二:還有別的 goroutine 活著 → goroutine leak
只要程式裡還有其他能跑的 goroutine(例如一個 HTTP server),runtime 就不會判定 deadlock。那條卡住的 goroutine 會永遠留著、佔記憶體不放,這叫 goroutine leak:
- 沒有錯誤訊息、沒有 panic
- 程式看起來活得好好的,只是某個功能沒反應
- 累積久了記憶體一路長
這是線上服務最難查的 bug 之一,也是為什麼寫 select 時要主動決定「等不到怎麼辦」,見 select 用 default 做非阻塞,用 time.After 做逾時。
順帶一提,空的 select{} 會永久阻塞——偶爾被拿來當「讓 main 永遠不結束」的寫法。