Golang之select中time.After()为什么不生效
今天下午写bug的时候写了这样一个demo:
func test() error {
ticker := time.NewTicker(time.Millisecond * 10)
defer ticker.Stop()
for {
select {
case <- ticker.C: // case1: 执行这个
if ok() {
fmt.Println("done")
return nil
}
fmt.Println("continue")
case <- time.After(time.Millisecond * 100): // case2: 这个永远不会被执行到
return errors.New("time out")
}
}
}
func ok() bool {
t := rand.Intn(3)
if t == 0 {
return true
}
return false
}
渴望的逻辑是执行case1数次之后如果一直不ok,就执行case2超时退出。
结果多次测试发现case2的time.After()一直执行不到,上下求索之后得出结论如下:
每次执行
time.After()都会创建一个新的timer的channel,而select语句是不可能记住它上次迭代的channel的。即case2的time.After()只在本次select时有效,下次select又重新从下次select的time.Now()开始计时了。(而且这种方式把一个异步操作变成了忙循环,违背了select语句的初衷。)
所以正确的操作应该是:
func test() error {
ticker := time.NewTicker(time.Millisecond * 10)
defer ticker.Stop()
timer := time.After(time.Millisecond * 100) // timer
for {
select {
case <- ticker.C:
if ok() {
fmt.Println("done")
return nil
}
fmt.Println("continue")
case <- timer: // 改这里
return errors.New("time out")
}
}
}
来源:
Each time you execute
time.After(4 * time.Second)you create a new timer channel. There's no way theselectstatement can remember the channel it selected on in the previous iteration. You've also taken what was an asynchronous operation and turned it into a busy loop, defeating the purpose of theselectstatement.https://stackoverflow.com/questions/39212333/how-can-i-use-time-after-and-default-in-golang
注:
需要写明的是,并非所有select中的time.After()都不生效,只是针对类似这种select有多个case可选的情况。
posted on 2020-10-14 16:22 GaiheiluKamei 阅读(1014) 评论(0) 收藏 举报
浙公网安备 33010602011771号