如果该 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 中主要做了这么几件事: