NewTimer倒计时计时器使用总结
使用案例
time.NewTimer()
是 Go 语言标准库 time
包中的一个函数,用于创建一个新的定时器(Timer)。
我们首先通过一个简单的例子,来展示计时器的使用。在这个例子中,我们将创建一个定时器。
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个定时器,设置为2秒后触发
timer := time.NewTimer(2 * time.Second)
fmt.Println("开始执行")
// 阻塞等待定时器到期
<-timer.C
fmt.Println("定时器到期")
}
我们再举一个稍微复杂一点的例子,假设有一个需求,要求在 5 秒钟内完成某个任务,否则就认为任务失败。这时我们就可以使用 NewTimer
来实现超时控制。
package main
import (
"fmt"
"time"
)
func main() {
//创建一个定时器,超时时间为5 秒钟
timer := time.NewTimer(5 * time.Second)
done := make(chan bool, 1)
//启动一个goroutine执行任务,并在任务完成后向通道发送一个完成信号
go func() {
//模拟任务执行耗时
time.Sleep(6 * time.Second)
done <- true
}()
select {
case <-done:
//任务完成,输出完成信息
fmt.Println("Task finished. ")
case <-timer.C:
//超时,输出超时信息
fmt.Println("Task timed out. ")
}
}
从timer.C取出的是当时的系统时间。
原理分析
分析time.NewTimer()
// The Timer type represents a single event.
// When the Timer expires, the current time will be sent on C,
// unless the Timer was created by AfterFunc.
// A Timer must be created with NewTimer or AfterFunc.
type Timer struct {
C <-chan Time
r runtimeTimer
}
// NewTimer creates a new Timer that will send
// the current time on its channel after at least duration d.
func NewTimer(d Duration) *Timer {
c := make(chan Time, 1)
t := &Timer{
C: c,
r: runtimeTimer{
when: when(d),
f: sendTime,
arg: c,
},
}
startTimer(&t.r)
return t
}
Timer.r是runtimeTimer结构体类型,这个名称带了runtime,大概率是和运行时包有关。
// Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/time.go:/^type timer
type runtimeTimer struct {
pp uintptr
when int64
period int64
f func(any, uintptr) // NOTE: must not be closure
arg any
seq uintptr
nextwhen int64
status uint32
}
从这个结构体的注释来看确实是在runtime包里面实现的。
下面来看看startTimer(&t.r)这个函数。
func startTimer(*runtimeTimer)
然后再点不进去。我们去runtime/time.go寻找可以发现如下代码。
// startTimer adds t to the timer heap.
//
//go:linkname startTimer time.startTimer
func startTimer(t *timer) {
if raceenabled {
racerelease(unsafe.Pointer(t))
}
addtimer(t)
}
它的作用是启动一个定时器,当定时器到期时,会执行相应的回调函数,即r.f,f这里传入的是sendTime回调函数。
// sendTime does a non-blocking send of the current time on c.
func sendTime(c any, seq uintptr) {
select {
case c.(chan Time) <- Now():
default:
}
}
在 startTimer
函数内部,会使用系统调用来启动一个底层的操作系统定时器,等到定时器超时时,底层系统会自动触发一个信号(例如 Unix 平台上的 SIGALRM 信号),然后该信号将由 Go 运行时内部的信号处理函数捕获,并最终调用相关的回调函数。