定义
func Every(t time.Duration, f func()) *time.Ticker {
ticker := time.NewTicker(t)
go func() {
for {
select {
case <-ticker.C:
f()
}
}
}()
return ticker
}
上述代码为go计时器的基础定义方式,略了解go则可以看出,通过从chan(ticker.C)中获取数据来触发func的调用来触发定时器,所以,关键问题是何时向chan中写入数据,至于周期时间如何计算并等待和唤醒暂不讨论。
解析
先来看看Ticker的定义
func NewTicker(d Duration) *Ticker {
if d <= 0 {
panic(errors.New("non-positive interval for NewTicker"))
}
// Give the channel a 1-element time buffer.
// If the client falls behind while reading, we drop ticks
// on the floor until the client catches up.
c := make(chan Time, 1)
t := &Ticker{
C: c,
r: runtimeTimer{
when: when(d),
period: int64(d),
f: sendTime,
arg: c,
},
}
startTimer(&t.r)
return t
}
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer
}
可以看到,Ticker.C被定义成单向只读,使用者仅能够读取该chan用以触发计时器,但实际上chan被初始化(buffer为1是为了写入数据不被阻塞)为双向的,而实际向chan写入数据则在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:
}
}
至此,计时器大致的流程能够有一个轮廓了,而关于计时器周期性的等待和唤醒像上文所说的,今天暂且不表,后期在陆续给出。