Go中定时器实现原理及源码解析 (4)

如果该 timer 状态是 timerModifiedEarlier 、timerModifiedLater,那么表示该 timer 的执行时间被修改过,需要重新调整它在最小堆中的位置,所以先调用 dodeltimer0 删除该 timer,再调用 doaddtimer 将该 timer 重新添加到最小堆。

runtime.runOneTimer

func runOneTimer(pp *p, t *timer, now int64) { ... // 需要被执行的函数 f := t.f // 被执行函数的参数 arg := t.arg seq := t.seq // 表示该 timer 为 ticker,需要再次触发 if t.period > 0 { // 放入堆中并调整触发时间 delta := t.when - now t.when += t.period * (1 + -delta/t.period) siftdownTimer(pp.timers, 0) if !atomic.Cas(&t.status, timerRunning, timerWaiting) { badTimer() } updateTimer0When(pp) // 一次性 timer } else { // 删除该 timer. dodeltimer0(pp) if !atomic.Cas(&t.status, timerRunning, timerNoStatus) { badTimer() } } unlock(&pp.timersLock) // 运行该函数 f(arg, seq) lock(&pp.timersLock) ... }

runOneTimer 会根据 period 是否大于0判断该 timer 是否需要反复执行,如果是的话需要重新调整 when 下次执行时间后重新调整该 timer 在堆中的位置。一次性 timer 的话会执行 dodeltimer0 删除该 timer ,最后运行 timer 中的函数;

timer 的触发

下面这里是我觉得比较有意思的地方,timer 的触发有两种:

从调度循环中直接触发;

另一种是Go语言的后台系统监控中会定时触发;

调度循环触发

调度循环,我在这篇文章 https://www.luozhiyun.com/archives/448 已经讲的很清楚了,不明白的同学可以自己再去看看。

整个调度循环会有三个地方去检查是否有可执行的 timer:

调用 runtime.schedule 执行调度时;

调用runtime.findrunnable获取可执行函数时;

调用runtime.findrunnable执行抢占时;

runtime.schedule

func schedule() { _g_ := getg() ... top: pp := _g_.m.p.ptr() ... // 检查是否有可执行 timer 并执行 checkTimers(pp, 0) var gp *g ... if gp == nil { gp, inheritTime = findrunnable() // blocks until work is available } ... execute(gp, inheritTime) }

下面我们看看 checkTimers 做了什么:

runtime.checkTimers

func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) { // 如果没有需要调整的 timer if atomic.Load(&pp.adjustTimers) == 0 { // 获取 timer0 的执行时间 next := int64(atomic.Load64(&pp.timer0When)) if next == 0 { return now, 0, false } if now == 0 { now = nanotime() } // 下次执行大于当前时间, if now < next { // 需要删除的 timer 个数小于 timer列表个数的4分之1,直接返回 if pp != getg().m.p.ptr() || int(atomic.Load(&pp.deletedTimers)) <= int(atomic.Load(&pp.numTimers)/4) { return now, next, false } } } lock(&pp.timersLock) // 进行调整 timer adjusttimers(pp) rnow = now if len(pp.timers) > 0 { if rnow == 0 { rnow = nanotime() } for len(pp.timers) > 0 { // 查找堆中是否存在需要执行的 timer if tw := runtimer(pp, rnow); tw != 0 { if tw > 0 { pollUntil = tw } break } ran = true } } // 如果需要删除的 timer 超过了 timer 列表数量的四分之一,那么清理需要删除的 timer if pp == getg().m.p.ptr() && int(atomic.Load(&pp.deletedTimers)) > len(pp.timers)/4 { clearDeletedTimers(pp) } unlock(&pp.timersLock) return rnow, pollUntil, ran }

checkTimers 中主要做了这么几件事:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpddsg.html