在业务中,我们经常需要基于定时任务来触发来实现各种功能,比如TTL会话管理,锁,定时任务(闹钟)或更复杂的状态切换等等。百纳网主要给大家介绍了关于Golang定时器陷阱的相关内容,所谓陷阱,就是它不是你认为的那样,这种认知误差可能让你的软件留下隐藏Bug。刚好计时器就有3个陷阱,我们会讲
1)重置的陷阱和
2)通道的陷阱,
3)停止的陷阱与重置的陷阱类似,自己探索吧。
下面话不多说了,来一起看看详细的介绍吧
<代码> Timer.Reset() 代码>函数的返回值是bool类型,我们看一个问题三连:
-
<李>它的返回值代表什么呢?李>
<李>我们想要的成功是什么?李>
<李>失败是什么?李>
成功:一段时间之后定时器超时,收到超时事件。
失败:成功的反面,我们收不到那个事件。对于失败,我们应当做些什么,确保我们的定时器发挥作用。
<强>重置的返回值是不是这个意思? 强>
通过查看文档和实现,<代码> Timer.Reset() 代码>的返回值并不符合我们的预期,这就是误差。它的返回值不代表重设定时器成功或失败,而是在表达定时器在重设前的状态:
-
<李>当计时器已经停止或者超时,返回错误的。李>
<李>当定时器未超时时,返回真的。李>
所以,当重置返回假时,我们并不能认为一段时间之后,超时不会到来,实际上可能会到来,定时器已经生效了。
如何跳过前面的陷阱,让重置符合我们的预期功能呢?直接忽视重置的返回值好了,它不能帮助你达到预期的效果。
真正的陷阱是计时器的通道,它和我们预期的成功,失败密切相关。我们所期望的定时器设置失败,通常只和通道有关:设置定时器前,定时器的通道Timer.C中是否已经有数据。
-
<李>如果有,我们设置的定时器失败了,我们可能读到不正确的超时事件。李>
<李>如果没有,我们设置的定时器成功了,我们在设定的时间得到超时事件。李>
接下来解释为何失败只与通道中是否存在超时事件有关。
定时器的缓存通道大小只为1,无法多存放超时事件,看源码。
//发送新创建一个新的计时器//当前时间> 如果len (Timer.C)比;0 { & lt; -Timer.C } Timer.Reset (time.Second)
测试代码
主要包 导入( “fmt” “时间” )//不同情况下,Timer.Reset()的返回值 func test1 () { fmt.Println(“第1个测试:重置返回值和什么有关?”) tm:=time.NewTimer (time.Second) 推迟tm.Stop () 戒烟:=(陈bool)//退出事件 去func () { 时间。睡眠(3 * time.Second) 退出& lt; -真的 }()//定时器未超时,看重置的返回值 如果! tm.Reset (time.Second) { fmt.Println(“未超时,重置返回错误的”) 其他}{ fmt.Println(“未超时,重置返回真正的“) }//停止计时器 tm.Stop () 如果! tm.Reset (time.Second) { fmt.Println(“停止计时器,重置返回错误的”) 其他}{ fmt.Println(“停止计时器,重置返回真正的“) }//定时器超时 为{ 选择{ 案例& lt;退出: 返回 案例& lt; -tm.C: 如果! tm.Reset (time.Second) { fmt.Println(“超时,重置返回错误的”) 其他}{ fmt.Println(“超时,重置返回真正的“) } } } } func test2 () { fmt.Println (“\ n第2个测试:超时后,不读通道中的事件,可以重置成功吗?”) sm2Start:=time.Now () tm2:=time.NewTimer (time.Second) 时间。睡眠(2 * time.Second) fmt。Printf(“重置前通道中事件的数量:% d \ n”, len (tm2.C)) 如果! tm2.Reset (time.Second) { fmt.Println(“不读通道数据,重置返回错误的”) 其他}{ fmt.Println(“不读通道数据,重置返回真正的“) } fmt。Printf(“重置后通道中事件的数量:% d \ n”, len (tm2.C)) 选择{ 例t:=& lt; -tm2.C: fmt。Printf (" tm2开始的时间:% v \ n”, sm2Start.Unix ()) fmt。Printf("通道中事件的时间:% v \ n”, t.Unix ()) 如果t.Sub (sm2Start) & lt;=time.Second +时间。毫秒{ fmt.Println(“通道中的时间是重新设置sm2前的时间,即第一次超时的时间,所以第二次复位失败了”) } } fmt。Printf("读通道后,其中事件的数量:% d \ n”, len (tm2.C)) tm2.Reset (time.Second) fmt。Printf("再次重置后,通道中事件的数量:% d \ n”, len (tm2.C)) 时间。睡眠(2 * time.Second) fmt。Printf("超时后通道中事件的数量:% d \ n”, len (tm2.C)) } func test3 () { fmt.Println (“\ n第3个测试:重置前清空通道,尽可能通畅”) smStart:=time.Now () tm:=time.NewTimer (time.Second) 时间。睡眠(2 * time.Second) 如果len (tm.C)比;0 { & lt; -tm.C } tm.Reset (time.Second)//超时 t:=& lt; -tm.C fmt。Printf (" tm开始的时间:% v \ n”, smStart.Unix ()) fmt。Printf("通道中事件的时间:% v \ n”, t.Unix ()) 如果t.Sub (smStart) & lt;=time.Second +时间。毫秒{ fmt.Println(“通道中的时间是重新设置sm前的时间,即第一次超时的时间,所以第二次复位失败了”) 其他}{ fmt.Println(“通道中的时间是重新设置sm后的时间,重置成功了”) } } 函数main () { test1 () test2 () test3 () }Golang中定时器的陷阱详解