This is a creation in Article, where the information may have evolved or changed.
This article simple introduction under the Golang Time packet timer implementation, said the timer, in our development process is very common, due to the use of different scenes, so the actual implementation of the timer is different, go timer is not used sigalarm signal implementation, but take the smallest heap of the way to achieve ( The source package uses an array implementation of the four-fork tree), using this method of timing accuracy is very high, but sometimes we do not need such a high-precision implementation, in order to more efficient use of resources, sometimes also achieve a lower precision algorithm.
There are several methods related to Golang timer:
<- Time.Tick( Time.Second)<- Time. After( Time.Second)<- Time.Newticker( Time.Second).C<- Time.Newtimer( Time.Second).C Time.Afterfunc( Time.Second, func() { /*do*/ }) Time.Sleep( Time.Second)
Here we take newticker as the entrance, the source code of Newticker is as follows:
func NewTicker(d Duration) *Ticker {if d <= 0 {panic(errors.New("non-positive interval for NewTicker"))}c := make(chan Time, 1)t := &Ticker{C: c,r: runtimeTimer{// when(d)返回一个runtimeNano() + int64(d)的未来时(到期时间)//runtimeNano运行时当前纳秒时间when: when(d),period: int64(d), // 被唤醒的时间f: sendTime, // 时间到期后的回调函数arg: c, // 时间到期后的断言参数},}// 将新的定时任务添加到时间堆中// 编译器会将这个函数翻译为runtime.startTimer(t *runtime.timer)// time.runtimeTimer翻译为runtime.timerstartTimer(&t.r)return t
Here's a more important thing is Starttimer (&T.R) Its implementation is translated in the runtime package
func startTimer(t *timer) {if raceenabled {racerelease(unsafe.Pointer(t))}addtimer(t)}func addtimer(t *timer) {lock(&timers.lock)addtimerLocked(t)unlock(&timers.lock)}
The code above to look convenient, I put them all together
The following code all writes out some comments
Use a lock to add a timer to the heap//start Timerprocfunc addtimerlocked (t *timer) {if T.when < 0 {T.when = 1<<63-1}//t.i If this method is run for the first time) I is the index in the timed task array//Append the new Timer task to the scheduled task array tail t.i = Len (timers.t) timers.t = append (timers.t, T)//four-tree minimum heap implemented using an array based on when (expiry time) Sort Siftuptimer (T.I)//If T.I index is 0if t.i = = 0 {if timers.sleeping {//If still awake at sleep timers.sleeping = false//here OS-based synchronization and OS System call//in Timerproc () to make goroutine recover from sleep notewakeup (&timers.waitnote)}if timers.rescheduling {timers.rescheduling = false//If there is no timer, Timerproc () and Goparkunlock common sleep//goready Here, the thread creates the stack, which is larger than the goroutine stack. The function cannot grow the stack and cannot be preempted by the scheduler Goready (TIMERS.GP, 0)}}if!timers.created {timers.created = Truego timerproc ()//This is only initialized once}}// Timerproc run-time-driven events. It sleep to the next in the timer heap. If AddTimer inserts a new event, it wakes the timerproc in advance. Func Timerproc () {timers.gp = GETG () for {lock (&timers.lock) timers.sleeping = Falsenow: = Nanotime () Delta: = Int64 (-1) For {if Len (timers.t) = = 0 {Delta = -1break}t: = Timers.t[0]delta = t.when-nowif Delta > 0 {break//time not to}if T.period > 0 {//Calculate next time//period wake-up interval T.when + = T.period * (1 +-delta/t.period) Siftdowntimer (0)} E LSE {//Remove from Heaplast: = Len (timers.t)-1if last > 0 {timers.t[0] = TIMERS.T[LAST]TIMERS.T[0].I = 0}timers.t[la ST] = NILTIMERS.T = Timers.t[:last]if Last > 0 {siftdowntimer (0)}t.i = 1//Tag removal}f: = T.farg: = t.argseq: = T.sequnloc K (&timers.lock) if raceenabled {raceacquire (unsafe. Pointer (t))}f (ARG, seq) lock (&timers.lock)}if Delta < 0 | | Faketime > 0 {//no timer, put Goroutine sleep. Timers.rescheduling = true//puts the current goroutine into a wait state and unlocks the lock. Goroutine can also be rerun by calling Goready (GP). Goparkunlock (&timers.lock, "Timer goroutine (Idle)", Traceevgoblock, 1) continue}//at least one timer pending. Sleep until then.timers.sleeping = Truetimers.sleepuntil = now + delta//reset Noteclear (&timers.waitnote) Unlock (& Timers.lock)//causes Goroutine to go to sleep until notewakeup is called,//Wakes NOTETSLEEPG (&timers.waitnote, Delta)} through Notewakeup)
The Golang uses a minimum heap (the minimum heap is a timer that satisfies the heap that is not smaller than the root node for each node that is not less than its parent node). Golang []*The timer structure is as follows:
Golang Storage Timing Task Structure
AddTimer inserts a value in the heap, and then maintains the minimum heap characteristics, in fact, this structure is an application of the minimum priority queue, and then convert the time to an absolute time processing, through sleep and wake to find the timing task, here to read the source code is easy, so only the code and some comments written out.
My Blog: Enoch | Noaway