根据文档... 对于使用 NewTimer 创建的计时器,仅应在通道已耗尽的停止或过期计时器上调用重置。
如果程序已经从 t.C 接收到一个值,则知道定时器已到期并且通道已耗尽,因此可以直接使用 t.Reset。但是,如果程序尚未从 t.C 接收到值,则必须停止计时器,并且如果 Stop 报告计时器在停止之前已过期,则通道显式耗尽。
为什么只有在定时器过期或停止时才需要调用重置?谁能举例说明如果不这样做可能会出现什么样的问题/错误。
我正在尝试识别 id 这种情况会导致运行时错误:无效的内存地址错误。但是,当我尝试这段代码时,我没有看到任何问题。 https://go.dev/play/p/mGUR__gjA_D
所以,我试图找出为什么我们应该确保计时器停止或过期。
我正在尝试识别 id,这种情况会导致运行时错误
不会的。计时器触发时运行的代码可能会引发此类错误,但调用
Reset
不会(除非您的逻辑中有错误并且 timer
是 nil
)。
任何人都可以举例说明如果不这样做可能会出现什么样的问题/错误。
这是一个例子游乐场
func main() {
timer1 := time.NewTimer(time.Millisecond)
timer2 := time.NewTimer(5 * time.Millisecond)
for i := 0; i < 10; i++ {
select {
case <-timer1.C:
fmt.Println("timer1")
// Lets assume this does something that takes a while.
time.Sleep(10 * time.Millisecond)
// The result of the action is that we want to reset both timers (timer2 should not fire)
timer1.Reset(time.Millisecond)
timer2.Reset(5 * time.Millisecond)
case <-timer2.C:
fmt.Println("timer2")
timer1.Reset(time.Millisecond)
timer2.Reset(5 * time.Millisecond)
}
}
}
浏览此代码,您可能会认为“当计时器 1 触发时,两个计时器都会重置,因此计时器 2 永远不会触发,输出将为”:
timer1
timer1
timer1
...
但实际情况是输出是:
timer1
timer2
timer1
timer2
...
这样做的原因是,当调用
timer2.Reset(5 * time.Millisecond)
时,timer2
已经被触发,因此在下一个循环中 case <-timer2.C
将被选择。问题是,除非您知道计时器已停止(并且必要时通道已耗尽),否则调用 Reset
的结果可能是不可预测的。请参阅此问题,了解有关一些相关问题的讨论。
这是一个相当人为的示例,因此很容易发现问题,但在实际代码中它并不总是显而易见的(文档做出此声明是有原因的,相信它!)。通过停止、耗尽然后重置计时器,您最终会得到一个已知的状态。按如下方式修改示例可提供我建议的可能预期的输出(playground):
func main() {
timer1 := time.NewTimer(time.Millisecond)
timer2 := time.NewTimer(5 * time.Millisecond)
for i := 0; i < 10; i++ {
select {
case <-timer1.C:
fmt.Println("timer1")
// Lets assume this does something that takes a while.
time.Sleep(10 * time.Millisecond)
// The result of the action is that we want to reset both timers (timer2 should not fire)
timer1.Reset(time.Millisecond) // We know timer1 has fired so don't need to stop it
resetActiveTimer(timer2, 5*time.Millisecond)
case <-timer2.C:
fmt.Println("timer2")
resetActiveTimer(timer1, time.Millisecond)
timer2.Reset(5 * time.Millisecond) // We know timer2 has fired so don't need to stop it
}
}
}
func resetActiveTimer(t *time.Timer, d time.Duration) {
if !t.Stop() {
<-t.C
}
t.Reset(d)
}