duancilan5124 2019-02-05 01:41
浏览 173
已采纳

在Golang中以最小间隔调用具有可变执行时间的函数的最佳方法?

I want to execute a function at no less than a given interval in Go, measured by the start of one invocation to the start of the next invocation. The function itself will vary in its execution time.

If the function runs for longer than that interval, I want to run it again immediately. But if/when it eventually resumes completing in less than a full interval, I want it to immediately resume waiting until the next interval-boundary.

For context, this is a rate-limiter-- the called function could easily spin the CPU but it wouldn't produce additional value, as it's interacting with humans who can't possibly react that fast.

An example for clarity (interval == 20ms in example):

runtime:  15ms
wait:      5ms
runtime:  25ms
wait:      0ms
runtime:  25ms
wait:      0ms
runtime:  15ms
wait:      5ms <-- this is the important bit

If I use time.Ticker, I believe additional "ticks" will either queue up in the Ticker.C channel (if it's buffered) causing it to make a bunch of invocations with no delay when it resumes, or the Ticker's writer will block on writing to the channel and end up resuming with an over-long delay for the first invocation afterit resumes.

Right now I'm doing some math, which is working, but it feels like it might be un-idiomatic:

minDurationBetweenRuns := time.Millisecond * 100
for {
    lastRunTime := time.Now()

    DO_STUFF_HERE()

    durationSinceLastRun := time.Now().Sub(lastRunTime)
    if durationSinceLastRun < minDurationBetweenRuns {
        sleepTime := minDurationBetweenRuns - durationSinceLastRun
        if sleepTime > minDurationBetweenRuns {
            sleepTime = minDurationBetweenRuns
        }
        time.Sleep(sleepTime)
    }
}
  • 写回答

1条回答 默认 最新

  • dongqiao1158 2019-02-05 01:41
    关注

    While writing the question, I remembered that the Golang source is super easy to read... and figured I should just have a peek before looking silly. I am pleased with what I found :)

    Comments in the source for time.Ticker say if the tick-reader falls behind it will start dropping ticks rather than blocking on writing to the channel (which has a buffer of only 1). The effect of this is to get things back "on track" immediately after we've missed one or more ticks.

    Example proof:

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        t := time.NewTicker(time.Millisecond * 50)
        for i := 0; i < 10; i++ {
            fmt.Printf("New invocation starting at %dms
    ", time.Now().Round(time.Millisecond).Nanosecond()/int(time.Millisecond))
            if i%3 == 0 {
                fmt.Println("Executing for 25ms")
                time.Sleep(time.Millisecond * 25)
            } else {
                fmt.Println("Executing for 75ms")
                time.Sleep(time.Millisecond * 75)
            }
            fmt.Println("Waiting for ticker...")
            <-t.C
        }
        t.Stop()
    }
    

    Output:

    New invocation starting at 0ms
    Executing for 25ms
    Waiting for ticker...
    New invocation starting at 50ms
    Executing for 75ms
    Waiting for ticker...
    New invocation starting at 125ms
    Executing for 75ms
    Waiting for ticker...
    New invocation starting at 200ms
    Executing for 25ms
    Waiting for ticker...
    New invocation starting at 250ms
    Executing for 75ms
    Waiting for ticker...
    New invocation starting at 325ms
    Executing for 75ms
    Waiting for ticker...
    New invocation starting at 400ms
    Executing for 25ms
    Waiting for ticker...
    New invocation starting at 450ms
    Executing for 75ms
    Waiting for ticker...
    New invocation starting at 525ms
    Executing for 75ms
    Waiting for ticker...
    New invocation starting at 600ms
    Executing for 25ms
    Waiting for ticker...
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 微信聊天记录备份到电脑提示成功了,但还是没同步到电脑微信
  • ¥15 python怎么在已有视频文件后添加新帧
  • ¥20 虚幻UE引擎如何让多个同一个蓝图的NPC执行一样的动画,
  • ¥15 fluent里模拟降膜反应的UDF编写
  • ¥15 MYSQL 多表拼接link
  • ¥15 关于某款2.13寸墨水屏的问题
  • ¥15 obsidian的中文层级自动编号
  • ¥15 同一个网口一个电脑连接有网,另一个电脑连接没网
  • ¥15 神经网络模型一直不能上GPU
  • ¥15 pyqt怎么把滑块和输入框相互绑定,求解决!