Go 1.23 新版本 Timer 和 Ticker 的重要优化

Timer 和 Ticker 的基本概念

在深入探讨 Go 1.23 版本对 Timer 和 Ticker 定时器进行的优化之前,以下是关于这两种定时器的基本介绍:

  • Timer:一次性定时器,用于在未来的某一时刻执行一次操作。常用于单次延迟执行任务。
  • Ticker:周期性定时器,用于在固定的时间间隔重复执行任务。常用于重复执行任务。

垃圾回收的改进

Go 1.23 之前的行为

在 Go 1.23 之前,如果一个 Timer 或 Ticker 没有被显式调用 Stop 方法,即使程序不再引用它们,它们也不会立即被垃圾回收。Timer 会在触发后被回收,而 Ticker 则从来不会被自动回收,这可能导致内存泄漏。

Go 1.23 新行为

Go 1.23 版本改进了这一点。如果程序不再引用一个 Timer 或 Ticker(即没有其他部分的代码持有它们的引用),即使没有调用 Stop 方法,它们也会有资格立即被垃圾回收。这减少了内存泄漏的风险。

计时器通道行为的变化

Go 1.23 之前的行为

在 Go 1.23 之前,与 Timer 或 Ticker 关联的通道带有一个元素缓冲区,这导致 Reset 或 Stop 方法在调用后,可能仍会接收到之前准备好的旧值。

Go 1.23 新行为

Go 1.23 版本中,计时器通道变成了无缓冲的(容量为 0)。这意味着在调用 Reset 或 Stop 方法后,Go 可以保证不会再接收到旧的值,使得 Reset 和 Stop 的使用更加可靠。

副作用

由于通道现在是无缓冲的,len 和 cap 操作返回的值变成了 0,而不是 1。这可能会影响那些依赖轮询通道长度来判断是否能成功接收值的代码。

示例代码

以下是在不同 Go 版本中的 Timer 行为的示例代码:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 程序退出信号
    quit := make(chan bool)
    timer := time.NewTimer(2 * time.Second)

    go func() {
        // 确保定时器已触发并发送信号
        time.Sleep(4 * time.Second)
        // 试图读取通道,看是否有值
        select {
        case t := <-timer.C:
            fmt.Println("接收到定时器信号:", t.Format(time.DateOnly))
        default:
            fmt.Println("无信号")
        }
        quit <- true
    }()
    
    // 确保定时器已触发并发送信号
    time.Sleep(3 * time.Second)
    wasStopped := timer.Stop()
    if wasStopped {
        fmt.Println("定时器未过期,停止成功")
    } else {
        fmt.Println("定时器已经过期并且信号已经发送")
    }
    // 等待退出信号
    <-quit
}

在 Go 1.22 及之前的版本的运行结果:

定时器已经过期并且信号已经发送
接收到定时器信号:2024-10-20

由于通道是有缓冲的,在定时器过期时已经发送了信号,因此即使在定时器触发之后调用 Stop() 方法,我们仍然可以从缓冲中接收到信号。

在 Go 1.23 或更高版本的运行结果:

定时器未过期,停止成功
无信号

由于通道是无缓冲的,信号发送是一个阻塞操作。如果在信号被接收之前调用 Stop() 方法,这将阻止信号的发送。因此,定时器被成功停止, Stop() 返回 true

注意事项

  • 对于 Timer 和 Ticker 的这些新行为只有在 Go 模块使用 go.mod 文件并且指定了 Go 1.23.0 或更高版本时才会生效。
  • 如果你在 go.mod 文件里指定的 Go 版本大于等于 Go 1.23,你可以通过设置环境变量 GODEBUGasynctimerchan=1,从而恢复到之前异步通道的行为。

总结

Go 1.23 版本中对 Timer 和 Ticker 进行了重要优化,包括两个主要方面:垃圾回收的改进和计时器通道行为的变化。改进后的垃圾回收机制有助于防止内存泄漏,而计时器通道的调整则确保在调用 Reset 或 Stop 之后,通道不会接收到任何旧数据,提高了定时器操作的可靠性和安全性。

打 赏