先看下面这段代码:funcmain(){timer:=time.NewTimer(3*time.Second)fmt.Println(time.Now(),"炸弹会3秒后引爆")timer.Stop()fmt.Println("定时炸弹已拆除,定时器无效")t:=<-timer.Cfmt.Println("炸弹引爆于",t)}复制代码先看运行结果。2021-08-2510:08:34.706412+0800CSTm=+0.023017601炸弹将在3秒内引爆。定时炸弹已经拆除,计时器已到。致命错误:所有goroutines都睡着了——死锁!要复制代码,我们可以在计时器到期之前使用Stop终止计时器。如果定时器已经停止,时间管道将永远无法读取数据。如果强行读取,就会发生死锁。因为使用Stop是停止向管道中写入数据,或者可以说管道中的数据已经读取完毕,而使用time.NewTimer(3*time.Second)是向管道中写入数据。我们正在看一个有趣的例子。funcmain(){timer:=time.NewTimer(1*time.Second)fmt.Println(time.Now())time.Sleep(2*time.Second)fmt.Println(time.Now())timer.Reset(10*time.Second)fmt.Println("Bombdetonatedat",<-timer.C)}复制代码现在,想想炸弹是什么时候引爆的!想知道答案吗?别着急,别着急,休息,休息一会儿,答案很快就会揭晓。来看看运行结果:2021-08-2510:15:16.8406335+0800CSTm=+0.0149998012021-08-2510:15:18.906213+0800CSTm=+2.080579301炸弹在2021-08-引爆2510:15:17.8522233+0800CSTm=+1.026589601复制代码和你想的一样吗?如果没有,没关系,你仔细听我说。因为time.sleep()是让主协程休眠,而timer.C读取的管道的协程是独立的。所以如果让主协程休眠,是不会影响定时器的计时的。相当于一颗即将引爆的定时炸弹。时间前后调整。诶!这时候你会说我不是reset了吗?但是定时器已经过期,所以复位不起作用。想想看,定时炸弹爆炸了,你去重置它还能用吗?如果我们将计时器设置为3秒,如下所示:timer:=time.NewTimer(3*time.Second)复制代码输出会是什么?2021-08-2510:26:21.1299417+0800CSTm=+0.020983301=+12.118733601复制代码并设置定时器后,主协程2秒后执行到Reset(),所以炸弹爆炸12设置定时器后的秒数。有趣的是,当我查看Reset()的源代码时,我发现了这样一段注释://Resetshouldbeinvokedonlyonstoppedorexpiredtimerswithdrainedchannels.//如果程序已经从t.C接收到一个值,则定时器已知//已过期且通道已耗尽,因此可以直接使用t.Reset。//如果程序尚未从t.C接收到值,//必须停止计时器并且-如果Stop报告计时器在停止之前过期//——通道显式耗尽////if!t.Stop(){//<-t.C//}//t.Reset(d)一般的想法是这样的,如果timer超时,t.C已经读取,可以直接reset。而如果程序Reset之前没有从t.C中读取过值,那么在使用reset之前需要调用Stop结束定时器。最后,如果您觉得这篇文章对您有点帮助,请点个赞。或者可以加入我的开发交流群:1025263163互相学习,我们会有专业的技术解答。如果您觉得这篇文章对您有用,请给我们的开源项目一个小星星:https://gitee。com/中邦科技非常感谢!PHP学习手册:https://doc.crmeb.com技术交流论坛:https://q.crmeb.com
