Golang中定时器的陷阱详解

  

  

在业务中,我们经常需要基于定时任务来触发来实现各种功能,比如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中定时器的陷阱详解