MST

星途 面试题库

面试题:Go中计时器(Timer)底层是如何实现定时功能的

在Go语言里,计时器(Timer)是一个常用的工具。请简要描述Go语言中计时器(Timer)底层是怎样实现定时功能的,包括涉及到的关键数据结构和主要流程。
50.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

关键数据结构

  1. Timer结构体
type Timer struct {
    C <-chan Time
    r runtimeTimer
}

C 是一个只读通道,当定时器到期时,会向该通道发送当前时间。runtimeTimer 是在运行时包中的结构体,是实现定时功能的核心数据结构。

  1. runtimeTimer结构体
type runtimeTimer struct {
    tb      *timersBucket
    i       int
    when    int64
    period  int64
    f       func(interface{}, uintptr)
    arg     interface{}
    seq     uintptr
}
  • tb:指向一个 timersBucket,用于管理一组定时器。
  • i:在 timersBucket 中的索引。
  • when:定时到期的时间(以纳秒为单位的绝对时间)。
  • period:如果是周期性定时器,代表周期时间(纳秒);非周期性定时器此值为0。
  • f:到期时执行的函数。
  • arg:传递给函数 f 的参数。
  • seq:用于区分不同定时器实例。
  1. timersBucket结构体
type timersBucket struct {
    lock  mutex
    gp    *g
    seq   uintptr
    timers []*runtimeTimer
    sleeping bool
    sleepingUntil int64
}
  • lock:互斥锁,用于保护对 timersBucket 内数据的并发访问。
  • gp:指向当前正在处理定时器的 goroutine(如果有的话)。
  • seq:递增的序列号,用于区分不同的定时器操作。
  • timers:存储 runtimeTimer 指针的切片。
  • sleeping:表示当前 timersBucket 是否在等待定时器到期。
  • sleepingUntil:下一个最早到期时间。

主要流程

  1. 创建定时器: 当调用 time.NewTimer 时,会创建一个 Timer 实例,并初始化 runtimeTimer 结构体的相关字段,如 when 设置为当前时间加上定时时间。然后,会将这个 runtimeTimer 加入到一个合适的 timersBucket 中,timersBucket 会根据 whentimers 切片进行排序。

  2. 定时等待time.NewTimer 返回的 Timer 实例的 C 通道会被阻塞,直到定时器到期。在底层,timersBucket 会根据当前最早到期的定时器时间,决定是否需要让管理它的 goroutine 睡眠。如果需要睡眠,会通过系统调用(如 nanosleep)进入睡眠状态,直到定时器到期或被提前唤醒。

  3. 定时器到期: 当定时器到期时间到达,管理 timersBucket 的 goroutine 被唤醒。它会遍历 timers 切片,找出所有到期的定时器。对于每个到期的定时器,如果是一次性定时器,会向 Timer 实例的 C 通道发送当前时间(如果 C 通道没有被关闭),并且将该定时器从 timersBucket 中移除;如果是周期性定时器,会重新计算下一次到期时间,并调整在 timers 切片中的位置,以便下次到期时再次触发。

  4. 取消定时器: 调用 Timer.Stop 方法可以取消定时器。在底层,会从 timersBucket 中移除对应的 runtimeTimer,如果 timersBucket 因为移除定时器而需要调整睡眠状态(如不再有定时器需要等待),会相应地处理。如果定时器已经到期但 C 通道还未被读取,Stop 方法会阻止向 C 通道发送数据。