4.4 定时器

    Timer 类型代表单次时间事件。当 Timer 到期时,当时的时间会被发送给 C (channel),除非 Timer 是被 AfterFunc 函数创建的。

    注意:Timer 的实例必须通过 NewTimerAfterFunc 获得。

    类型定义如下:

    C 已经解释了,我们看看 runtimeTimer。它定义在 sleep.go 文件中,必须和 runtime 包中 time.go 文件中的 timer 必须保持一致:

    我们通过 NewTimer() 来看这些字段都怎么赋值,是什么用途。

    • 如果 clock_gettime 不存在,则使用精度差些的系统调用 gettimeofday

    参数的值是 sendTime,定时器时间到时,会调用 f,并将 argseq 传给 f

    因为 Timer 是一次性的,所以 period 保留默认值 0。

    定时器的具体实现逻辑,都在 runtime 中的 time.go 中,它的实现,没有采用经典 Unix 间隔定时器 setitimer 系统调用,也没有 采用 POSIX 间隔式定时器(相关系统调用:timer_createtimer_settimetimer_delete),而是通过四叉树堆 (heep) 实现的(runtimeTimer 结构中的 i 字段,表示在堆中的索引)。通过构建一个最小堆,保证最快拿到到期了的定时器执行。定时器的执行,在专门的 goroutine 中进行的:go timerproc()。有兴趣的同学,可以阅读 runtime/time.go 的源码。

    通过 time.After 模拟超时:

    time.Stop 停止定时器 或 time.Reset 重置定时器

    会先调用 stopTimer 再调用 startTimer,类似于废弃之前的定时器,重新启动一个定时器。返回值和 Stop 一样。

    查看 runtime/time.go 文件中的 timeSleep 可知,Sleep 的是通过 Timer 实现的,把当前 goroutine 作为 arg 参数(getg())。

    TickerTimer 类似,区别是:Ticker 中的 runtimeTimer 字段的 period 字段会赋值为 NewTicker(d Duration) 中的 d,表示每间隔 d 纳秒,定时器就会触发一次。

    除非程序终止前定时器一直需要触发,否则,不需要时应该调用 Ticker.Stop 来释放相关资源。

    如果程序终止前需要定时器一直触发,可以使用更简单方便的 time.Tick 函数,因为 Ticker 实例隐藏起来了,因此,该函数启动的定时器无法停止。

    导航