业务需求:开一个循环处理外部发来的任务,如果一定时间内没有任务发过来就退出,处理完一个任务后刷新超时时间
然后我就写了这个
func handle(reqCh <-chan int) {
a := time.NewTimer(time.Second)
defer a.Stop()
for {
select {
case <-a.C:
fmt.Println("timeout", time.Now())
return
case req := <-reqCh:
fmt.Println(req) // 业务 可能耗时较多
fmt.Println(time.Now())
a.Reset(time.Second)
}
}
}
然后发现明明req一直在发没有超时,但handle还是退出了
迷糊了一下,然后就加上了打印时间测试一下,发现退出时间基本等于req处理完后的时间,所以问题找到了:Reset没有"生效"。
然后第一个想的办法是直接把Reset改成a = time.NewTimer(time.Second),问题是没有了,就是有点憨憨。
我认为go有一个比较好的地方,标准库的文档很详细,然后我就点开了Reset的文档。oh 获得了新知识....
- Reset有返回值. true和false含义不一样(这这里其实没有实际用处)
- NewTimer创建的Timer只能在Stop或者<-t.C以后调用Reset (我就是犯了这错)
然后官方给的建议是这个
if !t.Stop() {
<-t.C
}
t.Reset(d)
并且贴心地给了一句 This should not be done concurrent to other receives from the Timer's channel.
原因是如果t被stop两次<-t.C就block住了。好的,一般来说正常人不可能去stop两次,也不太会去并发stop,所以其实问题不大
不过最大的问题是太丑了... 居然有4行
然后因为文档说了Stop或者<-t.C以后才能Reset,那我直接Stop Reset不就好了,也没和文档说的矛盾嘿嘿。 (这样做有问题吗?有错误的话希望大佬指出)
t.Stop()
t.Reset(d)
最后总结一下
- time.Newtimer比较重,所以能Reset就Reset
- Reset之前必须Stop(或者执行<-t.C)
最后最后附上几个测试例子
func TestResetTimer(t *testing.T) {
fmt.Println(time.Now())
a := time.NewTimer(time.Millisecond * 500)
if !a.Stop() {
// 这里不会执行 Stop也有返回值 不多赘述
fmt.Println(111, time.Now())
fmt.Println(<-a.C)
}
a.Reset(time.Second)
<-a.C
fmt.Println(time.Now())
}
func TestResetTimer2(t *testing.T) {
a := time.NewTimer(time.Millisecond * 500)
a.Stop()
if !a.Stop() {
fmt.Println(111, time.Now())
fmt.Println(<-a.C) // fatal error: all goroutines are asleep - deadlock!
}
}
func BenchmarkResetAndNewTimer(b *testing.B) {
for i := 0; i < b.N; i++ {
a := time.NewTimer(time.Second)
select { // 加上这个是因为不能直接给a赋值
case <-a.C:
default:
}
a.Stop()
a.Reset(time.Second * 2) // 200ns/op
//a = time.NewTimer(time.Second * 2) // 900ns/op
}
}
标签:Reset,记录,fmt,Stop,timer,Println,time,go
From: https://www.cnblogs.com/xiaotushaoxia/p/17213250.html