問題的代碼如下,在for select 循環中,本想通過 time.After 設置超時時間,但一直無法退出。
package main import ( "fmt" "math/rand" "time" ) func main() { ch := make(chan int) go func() { for v := range ch { fmt.Println(v) } }() END: for { select { case ch <- rand.Int(): case <-time.After(time.Second * 3): fmt.Println("timeout") break END } time.Sleep(time.Second) } }
控制台始終打印出從通道 ch 中讀取的隨機數,time.After 一直沒能等到。
原因是每次進入到 case <-time.After(time.Second * 3): 時,都會創建一個新的通道返回,所以這個 case 每次都在等待接收新的通道的數據,永遠不可能等到。
time.After 方法的源碼如下:
func After(d Duration) <-chan Time { return NewTimer(d).C }
每次調用都會通過 NewTimer 創建一個新的 Timer,並且 Timer.C 也是新創建的通道。
func NewTimer(d Duration) *Timer { c := make(chan Time, 1) t := &Timer{ C: c, r: runtimeTimer{ when: when(d), f: sendTime, arg: c, }, } startTimer(&t.r) return t }
為了解決上面的問題,我們可以把 time.After 放在 for 循環外面,來解決此問題。
package main import ( "fmt" "math/rand" "time" ) func main() { ch := make(chan int) timer := time.After(time.Second * 3) go func() { for v := range ch { fmt.Println(v) } }() END: for { select { case ch <- rand.Int(): case <-timer: fmt.Println("timeout") break END } time.Sleep(time.Second) } }