面试题答案
一键面试关键数据结构
- Timer结构体:
type Timer struct {
C <-chan Time
r runtimeTimer
}
C
是一个只读通道,当定时器到期时,会向该通道发送当前时间。runtimeTimer
是在运行时包中的结构体,是实现定时功能的核心数据结构。
- 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
:用于区分不同定时器实例。
- 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
:下一个最早到期时间。
主要流程
-
创建定时器: 当调用
time.NewTimer
时,会创建一个Timer
实例,并初始化runtimeTimer
结构体的相关字段,如when
设置为当前时间加上定时时间。然后,会将这个runtimeTimer
加入到一个合适的timersBucket
中,timersBucket
会根据when
对timers
切片进行排序。 -
定时等待:
time.NewTimer
返回的Timer
实例的C
通道会被阻塞,直到定时器到期。在底层,timersBucket
会根据当前最早到期的定时器时间,决定是否需要让管理它的 goroutine 睡眠。如果需要睡眠,会通过系统调用(如nanosleep
)进入睡眠状态,直到定时器到期或被提前唤醒。 -
定时器到期: 当定时器到期时间到达,管理
timersBucket
的 goroutine 被唤醒。它会遍历timers
切片,找出所有到期的定时器。对于每个到期的定时器,如果是一次性定时器,会向Timer
实例的C
通道发送当前时间(如果C
通道没有被关闭),并且将该定时器从timersBucket
中移除;如果是周期性定时器,会重新计算下一次到期时间,并调整在timers
切片中的位置,以便下次到期时再次触发。 -
取消定时器: 调用
Timer.Stop
方法可以取消定时器。在底层,会从timersBucket
中移除对应的runtimeTimer
,如果timersBucket
因为移除定时器而需要调整睡眠状态(如不再有定时器需要等待),会相应地处理。如果定时器已经到期但C
通道还未被读取,Stop
方法会阻止向C
通道发送数据。