Go语言中time.After未按预期触发超时?代码问题咨询
Go select超时未按预期触发的原因分析
问题描述
我编写了如下Go代码:通过goroutine每秒向通道发送一次"result",共发送10次;主函数中使用select监听通道与time.After(3秒),预期3秒后打印"timeout"并退出,但实际是等10次"result"全部打印完成后才输出"timeout",请问这是为什么?
package main import "fmt" import "time" func main() { ch := make(chan string) go func() { for i:=0;i<10;i++ { time.Sleep(time.Second * 1) ch <- "result" } }() for { select { case res := <-ch: fmt.Println(res) case <-time.After(time.Second * 3): fmt.Println("timeout") return } } }
运行结果:
result
result
result
result
result
result
result
result
result
result
timeout
问题原因
问题核心在于**time.After的创建位置错误**:你把time.After(time.Second *3)放在了select代码块内部,每次循环执行到select时,都会重新创建一个全新的3秒定时器。
由于goroutine每秒都会向通道发送数据,select几乎每次都会优先命中case res := <-ch分支,处理完数据后进入下一次循环,再次生成新的3秒定时器。只有当10次数据全部发送完成后,通道没有新数据可读,select才会等待这次新创建的定时器触发——此时距离程序启动已经过了10秒,所以最后才打印"timeout"。
修复方案
将time.After的结果提前保存到变量中,让整个循环复用同一个定时器,确保3秒的计时从程序启动时开始计算:
package main import "fmt" import "time" func main() { ch := make(chan string) go func() { for i:=0;i<10;i++ { time.Sleep(time.Second * 1) ch <- "result" } }() // 提前创建定时器,复用同一个超时通道 timeout := time.After(time.Second * 3) for { select { case res := <-ch: fmt.Println(res) case <-timeout: fmt.Println("timeout") return } } }
修改后,定时器会在程序启动3秒后准时触发,直接执行超时分支退出程序,符合预期逻辑。
内容的提问来源于stack exchange,提问作者Dudadi
相关产品推荐
相关产品推荐

