go - 关于时间的准确性.Timer

标签 go timer

package main


import (
    "time"
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    TestTicker(wg)
    wg.Wait()
}

func TestTicker(wg sync.WaitGroup) {
    calDuration := func(duration time.Duration) time.Duration {
        now := time.Now()
        return now.Truncate(duration).Add(duration).Sub(now)
    }
    go func(){
        t := time.NewTimer(calDuration(time.Minute))
        for {
            <-t.C
            fmt.Println(time.Now())
            t.Reset(calDuration(time.Minute))
        }
        wg.Done()
    }()
}

它有时恰好每分钟滴答两次,因为持续时间可能会缩短。这真的很奇怪。有人可以帮帮我吗。谢谢

我只是在调用 TestTicker 时使用 waitgroup 来保持主函数。

我正在我的 MacOS 上运行测试代码

2018-07-19 14:36:00.003887996 +0800 CST m=+24.916092657
2018-07-19 14:37:00.002985076 +0800 CST m=+84.917119245
2018-07-19 14:38:00.001214551 +0800 CST m=+144.917278207
2018-07-19 14:39:00.000418561 +0800 CST m=+204.918411736
2018-07-19 14:39:59.999490194 +0800 CST m=+264.919412884
2018-07-19 14:40:00.000167519 +0800 CST m=+264.920090231
2018-07-19 14:40:59.99914446 +0800 CST m=+324.920996684
2018-07-19 14:41:00.000247228 +0800 CST m=+324.922099488

最佳答案

计时器的准确度可能因您的操作系统、硬件和 CPU 负载而异。虚拟机似乎特别不擅​​长提供准确的计时器(参见 https://github.com/golang/go/issues/14410)。不幸的是,您没有提到您在什么环境中运行此代码。

如果您可以忍受这些不准确,并且仍然需要您的代码在整整一分钟内做一些事情,您的代码就会中断,因为当间隔太短时(14:39:59.999490194 仅比 14:40 短 500µs,calDuration 将使其等待几微秒,直到下一个整分钟。为了解决这个问题,您需要使用 Duration.Round 而不是 Duration.Truncate

另外不要忘记 t.C 会返回计时器触发的时间,因此您需要在调用 calDuration 时使用此值(这也会为您节省昂贵的系统调用)。

func TestTicker(wg *sync.WaitGroup) {
    calDuration := func(now time.Time, duration time.Duration) time.Duration {
        return now.Round(duration).Add(duration).Sub(now)
    }
    go func(){
        t := time.NewTimer(calDuration(time.Now(), time.Minute))
        for {
            now := <-t.C
            fmt.Println(now)
            t.Reset(calDuration(now, time.Minute))
        }
        wg.Done()
    }()
}

另一种方法是使用标准库中的 time.Ticker 并在启动自动收报机之前发出适当的 sleep ,以便它在一整分钟内计时:

func TestTicker(wg *sync.WaitGroup) {
    go func(interval time.Duration) {
        // wait until next time interval
        now := time.Now()
        time.Sleep(now.Truncate(interval).Add(interval).Sub(now))
        // get time of first beat
        now = time.Now()
        // start the ticker
        t := time.NewTicker(interval)
        for {
            fmt.Println(now)
            now = <-t.C
        }
        wg.Done()
    }(time.Minute)
}

关于go - 关于时间的准确性.Timer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51415965/

相关文章:

go - 在go中使用函数作为if语句的参数

go - 包的`init()`函数中的无限for循环–好还是坏主意?

去 GC 停止我的 goroutine?

ios - 使应用程序在后台删除和更新核心数据( objective-c )

java - java 中的秒表

C++定时器函数,每1秒刷新一次

go - 为什么我们在 Go 调度器的设计中添加 P 而不是简单地更改 M?

json - 在 JSON get 请求中设置 header

c# - 在 Windows 窗体应用程序中使用计时器

ios - 将按钮连接到计时器并以百分比计算时间