本节咱们从Ticker数据结构入手,结合源码分析Ticker的实现原理。git
实际上,Ticker与以前讲的Timer几乎彻底相同,不管数据结构和内部实现机制都相同,惟一不一样的是建立方式。github
Timer建立时,不指定事件触发周期,事件触发后Timer自动销毁。而Ticker建立时会指定一个事件触发周期,事件会按照这个周期触发,若是不显式中止,定时器永不中止。数据结构
Ticker数据结构与Timer除名字不一样外彻底同样。函数
源码包src/time/tick.go:Ticker
定义了其数据结构:源码分析
type Ticker struct { C <-chan Time // The channel on which the ticks are delivered. r runtimeTimer }
Ticker只有两个成员:ui
这里应该按照层次来理解Ticker数据结构,Ticker.C即面向Ticker用户的,Ticker.r是面向底层的定时器实现。code
runtimeTimer也与Timer同样,这里再也不赘述。协程
咱们来看建立Ticker的实现,很是简单:blog
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), // Ticker跟Timer的重要区就是提供了period这个参数,据此决定timer是一次性的,仍是周期性的 f: sendTime, arg: c, }, } startTimer(&t.r) return t }
NewTicker()只是构造了一个Ticker,而后把Ticker.r经过startTimer()交给系统协程维护。接口
其中period为事件触发的周期。
其中sendTime()方法即是定时器触发时的动做:
func sendTime(c interface{}, seq uintptr) { select { case c.(chan Time) <- Now(): default: } }
sendTime接收一个管道做为参数,其主要任务是向管道中写入当前时间。
建立Ticker时生成的管道含有一个缓冲区(make(chan Time, 1)
),可是Ticker触发的事件确是周期性的,若是管道中的数据没有被取走,那么sendTime()也不会阻塞,而是直接退出,带来的后果是本次事件会丢失。
综上,建立一个Ticker示意图以下:
中止Ticker,只是简单的把Ticker从系统协程中移除。函数主要实现以下:
func (t *Ticker) Stop() { stopTimer(&t.r) }
stopTicker()即通知系统协程把该Ticker移除,即再也不监控。系统协程只是移除Ticker并不会关闭管道,以免用户协程读取错误。
与Timer不一样的是,Ticker中止时没有返回值,即不须要关注返回值,实际上返回值也没啥用途。
综上,中止一个Ticker示意图以下:
Ticker没有重置接口,也即Ticker建立后不能经过重置修改周期。
须要格外注意的是Ticker用完后必须主动中止,不然会产生资源泄露,会持续消耗CPU资源。
赠人玫瑰手留余香,若是以为不错请给个赞~
本篇文章已归档到GitHub项目,求星~ 点我即达