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...
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 请问这个是什么意思?
  • ¥15 STM32驱动继电器
  • ¥15 Windows server update services
  • ¥15 关于#c语言#的问题:我现在在做一个墨水屏设计,2.9英寸的小屏怎么换4.2英寸大屏
  • ¥15 模糊pid与pid仿真结果几乎一样
  • ¥15 java的GUI的运用
  • ¥15 我想付费需要AKM公司DSP开发资料及相关开发。
  • ¥15 怎么配置广告联盟瀑布流
  • ¥15 Rstudio 保存代码闪退
  • ¥20 win系统的PYQT程序生成的数据如何放入云服务器阿里云window版?