当前位置: 首页 > 科技观察

Go语言一次性定时器使用及实现原理

时间:2023-03-11 20:55:02 科技观察

1.在Go语言标准库时间包中引入Timer类型,它是代表单个事件的定时器,也就是说是一次性定时器。在Go语言项目开发中,定时器被广泛使用。在这篇文章中,我们将介绍如何使用Go语言中的Timer及其实现原理。2.如何使用要使用Timer一次性定时器,需要导入时间包。创建定时器有两种方式,分别是:funcNewTimer(dDuration)*Timer使用funcNewTimer创建一个Timer,入参为定时器等待的时间,当时间到达时,将当前时间发送给这个频道。示例代码:funcmain(){nowTime:=time.Now().Unix()fmt.Println(nowTime)NewTimerDemo()}funcNewTimerDemo(){timer:=time.NewTimer(2*time.Second)select{case<-timer.C:currentTime:=time.Now().Unix()fmt.Println(currentTime,"dosomething")}}输出结果:16658941361665894138dosomething通过阅读上面的代码,我们可以发现我们定义创建一个2s后执行的timer定时器,然后使用select读取timer.C中的数据,读取到数据后执行具体的业务逻辑代码。funcAfterFunc(dDuration,ffunc())*Timer使用funcAfterFunc创建一个Timer,入参为定时器的等待时间,以及时间到要执行的函数。示例代码:funcmain(){nowTime:=time.Now().Unix()fmt.Println(nowTime)AfterFuncDemo()}funcAfterFuncDemo(){time.AfterFunc(2*time.Second,func(){currentTime:=time.Now().Unix()fmt.Println(currentTime,"dosomething")})time.Sleep(3*time.Second)}看完上面的代码,细心的读者可能已经注意到了,我们使用代码最后的time.Sleep(),因为time.AfterFunc()是异步执行的,所以需要等待协同退出。3.实现原理我们查看源码中Timer的数据结构,发现它包含两个字段,其中一个是可导出字段C,是一个Time类型的chan;另一个是不可导出的字段r,这是一个runtimeTimer类型。typeTimerstruct{C<-chanTimerruntimeTimer}其实每个Go应用底层都会有一个特定的协程管理Timer,这个协程(bottomcoroutine)监听一个Timer指定时间的到来,当前时间会发送给C,然后当上层读取到C中的数据时,就会执行相关的业务逻辑代码。底层协程监听Timer的r域中的数据。我们查看一下源码中runtimeTimer的数据结构:runtimeTimer中包含的字段,我们重点关注when、f和arg。when:定时器执行时间。f:定时器执行的回调函数。arg:定时器执行的回调函数的参数。简单了解了Timer的数据结构之后,我们来看一下源码中funcNewTimer的代码://NewTimer创建一个新的Timer,它会在至少durationd.funcNewTimer(dDuration)*Timer{c:=make(chanTime,1)t:=&Timer{C:c,r:runtimeTimer{when:when(d),f:sendTime,arg:c,},}startTimer(&t.r)returnt}看了上面的代码,我们可以发现funcNewTimer的实现很简单,其实就是构造一个Timer,然后将Timer.r传递给startTimer(),除了startTimer()除了函数之外还有两个函数,分别是when()和sendTime,其中when()是计算定时器的执行时间,sendTime是定时器时间到达时执行的事件(其实就是当前的时间被写入通道)。sendTime源代码:funcsendTime(cinterface{},sequintptr){//在c上非阻塞发送时间。//在NewTimer中使用,无论如何都不能阻塞(buffer)。//在NewTicker中使用,将发送放在地板上是//当读者落后时所需的行为,//因为发送是周期性的。select{casec.(chanTime)<-Now():default:}}我们了解到funcNewTimer会构造Timer.r传参给startTimer(),startTimer()负责将runtimeTimer写入底层的数组coroutine(如果底层协程没有运行,会启动底层协程),并将Timer交给底层协程Process监听,也就是上面说的,当底层协程监听到一个Timer指定的时间到了,它发送当前时间到它的频道。//startTimer将t添加到定时器堆。//go:linknamestartTimertime.startTimerfuncstartTimer(t*timer){ifraceenabled{racerelease(unsafe.Pointer(t))}addtimer(t)}4.本文总结我们介绍了Go语言标准库的time包提供的一次性定时器Timer,不仅介绍了它的用法,还介绍了它的实现原理。