计时器分 Timer 和 Ticker 两种,它们底层基本是一样的,两差的区别请参考 https://blog.haohtml.com/archives/19859, 这里我们的介绍对象是 Timer 。

计时器结构体
https://github.com/golang/go/blob/go1.17.6/src/time/sleep.go#L84-L98
// 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 }
通过调用 NewTimer()
函数创建一个 Timer
,首先创建一个长度为1的有缓冲channel,再创建一个Timer
的结构体,并将 channel 置于 Timer 结构体内。
注意这里的 runtimeTimer.f 字段是一个函数 sendTime ,其实现如下
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: } }
当 sendTime 函数主要用在 newTimer() 时,它以无阻塞的方式将当前时间 Now() 发送到 c 通道里。如果用在 newTicker() 时,如果读取落后,会将发送丢弃,它是周期性的。
我们给出 Timer 的结构体声明。
type Timer struct {
C <-chan Time
r runtimeTimer
}
一共两个字段,为了理解方面我们称 runtimeTimer
为 timer 值。
我们再看一下其中的 runtimeTimer
结构体的声明
// Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/time.go:/^type timer
type runtimeTimer struct {
pp uintptr
when int64
period int64
f func(interface{}, uintptr) // NOTE: must not be closure
arg interface{}
seq uintptr
nextwhen int64
status uint32
}
对于 runnerTimer
结构体要与在 runtime/time.go 文件中的 timer
结构体保持同步。
结构体字段说明
- pp 指针类型,这里指 GPM 中的 P。如果这个计时器 timer 在一个heap 上,它在哪个 P 的堆上
- when 表示唤醒执行的时间,表示什么时间开始执行
- period 周期,一定是大于
0
; when+period 表示下次唤醒执行的时间 - f 执行函数,不允许为匿名函数,最好为非阻塞函数
- arg 上面f函数的参数
- seq 同 arg,其在 runOneTimer 函数中的调用方式为 f(arg, seq)
- nextwhen 下次运行的时间,其值只有在
timerModifiedXX status
状态下才设置 - status 状态,其定义的的可用值有10种,定义在 runtime/time.go,我们下面对这些状态进行了介绍。
每次开启一个goroutine 执行 f(arg, now),基中when表示执行的时间,而 when+period 表示下次执行的时间。(这时有点疑问,对调用的函数参数,f的第二个参数是 now, 但后面介绍的时候第二个参数却是 seq)
通过查看 https://github.com/golang/go/blob/go1.17.6/src/runtime/time.go#L41-L116 可知以下几点:
Continue reading