线上听了欧神的分享,就当算是个笔记吧。
Timer顾名思义就是定时器的意思,比如我们可以不借助消息队列中间件,单纯的用timer也可以实现一些定时功能。用法不提了,写一下1.4的分析。
type timer struct { // If this timer is on a heap, which P's heap it is on. // puintptr rather than *p to match uintptr in the versions // of this struct defined in other packages. pp puintptr // 这里主要是和P绑定,减少数据竞争 // Timer wakes up at when, and then at when+period, ... (period > 0 only) // each time calling f(arg, now) in the timer goroutine, so f must be // a well-behaved function and not block. when int64 period int64 f func(interface{}, uintptr) arg interface{} seq uintptr // What to set the when field to in timerModifiedXX status. nextwhen int64 // The status field holds one of the values below. status uint32 }
// The Timer type represents a single event. // When the Timer expires, the current time will be sent on C, // unless the Timer was created by AfterFunc. // A Timer must be created with NewTimer or AfterFunc. type Timer struct { C <-chan Time // 这就是典型的管道通信,在一个P里。 r runtimeTimer }
// NewTimer creates a new Timer that will send // the current time on its channel after at least duration d. func NewTimer(d Duration) *Timer { c := make(chan Time, 1) t := &Timer{ C: c, r: runtimeTimer{ when: when(d), f: sendTime, arg: c, }, } startTimer(&t.r) return t }
func sendTime(c interface{}, seq uintptr) { // Non-blocking send of time on c. // Used in NewTimer, it cannot block anyway (buffer). // Used in NewTicker, dropping sends on the floor is // the desired behavior when the reader gets behind, // because the sends are periodic. select { case c.(chan Time) <- Now(): default: } }
type p struct { (...) timersLock mutex timers []*timer // 虽然是数组,但实际上底层是堆实现 (...) }
func schedule() { _g_ := getg() (...) top: pp := _g_.m.p.ptr() (...) checkTimers(pp, 0) // 每次调度都要查看这个p绑定的timer (...) execute(...) }
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 { return t.when } (...) runOneTimer(pp, t, now) return 0 (...) } } }
func runOneTimer(pp *p, t *timer, now int64) { (...) f := t.f arg := t.arg seq := t.seq dodeltimer0(pp) atomic.Cas(&t.status, timerRunning, timerNoStatus) (...) unlock(&pp.timersLock) f(arg, seq) // 触发 sendTime 信号 通知用户 goroutine lock(&pp.timersLock) (...) }
欧神画的Timer状态机
实际上细枝末节的东西还很多,比如把当前的timer要执行的g分到别的p去执行等。
https://changkun.de/golang/zh-cn/part2runtime/ch06sched/timer/
end