• Home
  • Articles
    • 日志
    • 妍小言
    • 舒小书
    • 浩然说
    • 生活日记
  • All Tags

Go定时器

26 Nov 2019

Reading time ~1 minute

定义

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:
	}
}

至此,计时器大致的流程能够有一个轮廓了,而关于计时器周期性的等待和唤醒像上文所说的,今天暂且不表,后期在陆续给出。



gotimer