@TOC
定时器
time/sleep.go中runtimeTimer的结构体定义:
type runtimeTimer struct {
pp uintptr
when int64 // 当前定时器下次被触发的绝对时间
period int64 // 当前定时器周期性触发间隔(Timer的为0)
f func(any, uintptr) // 定时器触发时执行的回调函数(仅接收arg和seq两个参数)
arg any // 回调函数的参数1
seq uintptr // 回调函数的参数2
// Timer不使用该参数,且仅在网络收发场景下使用)
nextwhen int64
status uint32
}
1)runtimeTimer由goroutine对应的P进行维护;
2)每创建个Timer/Ticker就等于创建个runtimeTimer;
3)runtimeTimer存在数组中,并根据when字段进行堆排序;
Timer
一次性定时器(Timer):经历指定时间后触发事件(自动关闭)
1)常配合select实现程序的超时和程序的延迟执行;
创建Timer的格式(引入time包):Timer对象 := time.NewTimer(时间)
1)Timer创建后立刻开始计时(无须其他启动命令);
2)Timer创建后会交由本goroutine对应的P进行维护;
3)Timer对象在指定时间会向自身的C通道发送当时的系统时间;
常用方法/函数:
1)停止Timer的计时(并不会关闭通道C)
func (t *Timer) Stop() bool
// 若t已被关闭或过期则返回false(成功返回true)
2)重置Timer的计时为d
func (t *Timer) Reset(d Duration) bool
// 可激活已被停止或过期的Timer
// 若t已被关闭或过期则返回false(成功返回true)
3)创建匿名Timer,仅返回匿名Timer的通道C
func After(d Duration) <-chan Time
// 匿名Timer无法被关闭(慎用)
4)创建Timer,并在过期后调用函数f
func AfterFunc(d Duration, f func() ) *Timer
// 函数f是异步执行的(主goroutine需设置时间等待)
// 可通过返回Timer的Stop()方法取消等待和对函数f的调用
实现原理
time/sleep.go中Timer的结构体定义:
type Timer struct {
C <-chan Time // 应用于用户
r runtimeTimer // 应用于底层定时器
}
time/sleep.go中NewTimer()函数的定义:
func NewTimer(d Duration) *Timer {
c := make(chan Time, 1) // 创建管道
t := &Timer{ // 初始化Timer(根据runtimeTimer)
C: c, // 赋值管道
r: runtimeTimer{
when: when(d), // 指定触发的绝对时间
f: sendTime, // 触发后执行sendTime函数
arg: c, // sendTime的参数
},
}
startTimer(&t.r) // 启动定时器(将runtimeTimer交由P进行维护)
return t
}
1)创建缓冲区为1的管道,使senTime()函数永不阻塞;
time/sleep.go中sendTime()函数的定义:
func sendTime(c interface{}, seq uintptr) {
select {
case c.(chan Time) <- Now():
default:
}
}
1)select搭配的空defalut分支,使sendTime()函数永不阻塞;
2)若多次触发相同Timer且管道中还有值,会导致后续触发事件遗失;
Ticker
周期性定时器(Ticker):周期性触发事件(手动关闭)
1)配合for-range和select分别实现程序的循环定时任务和聚合任务;
创建Ticker的格式(引入time包):Ticker对象 := time.NewTicker(时间)
1)Ticker创建后立刻开始计时(无须其他启动命令);
2)Ticker创建后会交由本goroutine对应的P进行维护;
3)Ticker对象在指定时间会向自身的C通道发送当时的系统时间;
4)创建Ticker对象后应使用defer和其Stop()方法确保其被关闭;
//Ticker未被关闭会导致资源泄露(其不会自动关闭)
常用方法/函数:
1)停止Ticker对象
func (t *Ticker) Stop()
// 不会关闭通道C(通道C会在数据被读取完后,自动关闭)
2)创建匿名Ticker,仅返回匿名Ticker的通道C
func (d Duration) <-chan Time
// 该Ticker无法被关闭(慎用)
实现原理
time/tick.go中Ticker的结构体定义:
type Ticker struct {
C <-chan Time // 应用于用户
r runtimeTimer // 应用于底层定时器
}
time/tick.go中NewTicker()函数的定义:
func NewTicker(d Duration) *Ticker {
if d <= 0 {
panic(errors.New("non-positive interval for NewTicker"))
}
c := make(chan Time, 1)
t := &Ticker{
C: c,
r: runtimeTimer{
when: when(d),
period: int64(d), // Timer与Ticker的唯一不同之处
f: sendTime,
arg: c,
},
}
startTimer(&t.r)
return t
}
1)Ticker创建后不可修改其周期;
2)Ticker使用完毕后务必主动调用其Stop()方法(配合defer语句);
time/tick.go中sendTime()函数的定义:
func sendTime(c interface{}, seq uintptr) {
select {
case c.(chan Time) <- Now():
default:
}
}
1)select搭配的空defalut分支,使sendTime()函数永不阻塞;
2)若多次触发相同Timer且管道中还有值,会导致后续触发事件遗失;