如果在尚未过期或停止的计时器上调用timer.Reset(),会发生什么情况

问题描述 投票:0回答:1

根据文档... 对于使用 NewTimer 创建的计时器,仅应在通道已耗尽的停止或过期计时器上调用重置。

如果程序已经从 t.C 接收到一个值,则知道定时器已到期并且通道已耗尽,因此可以直接使用 t.Reset。但是,如果程序尚未从 t.C 接收到值,则必须停止计时器,并且如果 Stop 报告计时器在停止之前已过期,则通道显式耗尽。

为什么只有在定时器过期或停止时才需要调用重置?谁能举例说明如果不这样做可能会出现什么样的问题/错误。

我正在尝试识别 id 这种情况会导致运行时错误:无效的内存地址错误。但是,当我尝试这段代码时,我没有看到任何问题。 https://go.dev/play/p/mGUR__gjA_D

所以,我试图找出为什么我们应该确保计时器停止或过期。

go goroutine
1个回答
0
投票

我正在尝试识别 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)
}
© www.soinside.com 2019 - 2024. All rights reserved.