golang 源码学习之timer/ticker
/// runtime/proc.go
func schedule() {
...
pp := _g_.m.p.ptr() // 获取当前P
...
checkTimers(pp, 0)
}
func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) {
...
lock(&pp.timersLock)
adjusttimers(pp)
rnow = now
if len(pp.timers) > 0 {
if rnow == 0 {
rnow = nanotime()
}
for len(pp.timers) > 0 { // timers集合大于0,循环
if tw := runtimer(pp, rnow); tw != 0 {
if tw > 0 {
pollUntil = tw
}
break // checkTimers结束
}
ran = true
}
}
...
unlock(&pp.timersLock)
return rnow, pollUntil, ran
}
func runtimer(pp *p, now int64) int64 {
for {
t := pp.timers[0] // 最小堆的第一个
switch s := atomic.Load(&t.status); s {
case timerWaiting:
if t.when > now { // 时间还没到,返回到期时间,checkTimers的for循环也会终止
return t.when
}
if !atomic.Cas(&t.status, s, timerRunning) {
continue
}
runOneTimer(pp, t, now) // 时间到期
return 0
.....
}
}
}
func runOneTimer(pp *p, t *timer, now int64) {
f := t.f // timer的回调方法,创建时候设置的sendTime
arg := t.arg // 创建时候设置的通道
seq := t.seq
if t.period > 0 { // t.period > 0说明是ticker
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) // 更新P的到达时间
} else { // 如果不是ticker 则移除出最小堆
dodeltimer0(pp)
if !atomic.Cas(&t.status, timerRunning, timerNoStatus) {
badTimer()
}
}
unlock(&pp.timersLock)
f(arg, seq) // 执行sendTime
lock(&pp.timersLock)
}