首先等待时间。AfterFunc然后开始时间。NewTicker

时间:2019-08-18 10:34:35

标签: go

我正在尝试建立一个每小时运行一次的服务例程。在我看来,这两个都很容易。要在小时内运行例程,可以使用time.AfterFunc(),首先计算到小时数为止的剩余时间。为了每小时运行一次例程,我可以使用time.NewTicker()

但是,只有在传递给NewTicker的函数被触发后,我才能努力找出如何启动AfterFunc()

我的main()函数看起来像这样:

func main() {
    fmt.Println("starting up")

    // Here I'm setting up all kinds of HTTP listeners and gRPC listeners, none
    // of which is important, save to mention that the app has more happening
    // than just this service routine.

    // Calculate duration until next hour and call time.AfterFunc()
    // For the purposes of this exercise I'm just using 5 seconds so as not to
    // have to wait increments of hours to see the results
    time.AfterFunc(time.Second * 5, func() {
        fmt.Println("AfterFunc")
    })

    // Set up a ticker to run every hour. Again, for the purposes of this
    // exercise I'm ticking every 2 seconds just to see some results
    t := time.NewTicker(time.Second * 2)
    defer t.Stop()
    go func() {
        for now := range t.C {
            fmt.Println("Ticker")
        }
    }()

    // Block until termination signal is received
    osSignals := make(chan os.Signal, 1)
    signal.Notify(osSignals, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
    <-osSignals

    fmt.Println("exiting gracefully")
}

当然time.AfterFunc()正在阻塞,而我的Ticker的有效负载被故意放入go例程中,因此它也不会阻塞。这样,我的HTTP和gRPC侦听器可以继续侦听,但也允许main()末尾的代码块根据来自操作系统的终止信号而正常退出。但是现在明显的缺点是,Ticker会立即启动,并在函数传递给AfterFunc()之前触发两次(间隔2秒)触发。输出看起来像这样:

Ticker
Ticker
AfterFunc
Ticker
Ticker
Ticker
etc.

我当然想要的是:

AfterFunc
Ticker
Ticker
Ticker
Ticker
Ticker
etc.

以下内容也不起作用,我不确定为什么。它会打印AfterFunc,但不会触发代码。

time.AfterFunc(time.Second * 5, func() {
        fmt.Println("AfterFunc")

        t := time.NewTicker(time.Second * 2)
        defer t.Stop()
        go func() {
            for now := range t.C {
                fmt.Println("Ticker")
            }
        }()
    })

3 个答案:

答案 0 :(得分:1)

  

The Go Programming Language Specification

     

Program execution

     

程序执行从初始化主程序包开始,然后   调用函数main。当该函数调用返回时,   程序退出。它不等待其他(非主)goroutine进入   完成。

time.AfterFunc(time.Second * 5, func() {
    fmt.Println("AfterFunc")
    t := time.NewTicker(time.Second * 2)
    defer t.Stop()
    go func() {
        for now := range t.C {
            fmt.Println("Ticker")
        }
    }()
})

defer t.Stop()停止了股票行情。

您不是在等待goroutine运行。

答案 1 :(得分:0)

天哪,我在发布后不久就发现了这一点,尽管我仍然认为我的解决方案缺乏优雅之处。

@peterSO指出,传递给AfterFunc()的函数在用Ticker创建后执行并停止defer t.Stop()片刻。

对我来说,解决方案是在调用t之前定义AfterFunc()变量,以使其在AfterFunc()有效负载函数之外具有作用域,然后在结束时将其停止我的main()函数。这是新的main()函数:

func main() {
    fmt.Println("starting up")

    var t *time.Ticker
    time.AfterFunc(time.Second * 5, func() {
        fmt.Println("AfterFunc")

        t = time.NewTicker(time.Second * 2)
        go func() {
            for now := range t.C {
                fmt.Println("Ticker")
            }
        }()
    })
    //defer t.Stop()

    // Block until termination signal is received
    osSignals := make(chan os.Signal, 1)
    signal.Notify(osSignals, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
    <-osSignals

    t.Stop()

    logger.Info("exiting gracefully")
}

奇怪的是,尽管注释defer t.Stop()导致应用程序因终止信号而关闭,但会导致紧急情况(无效的内存地址或nil指针取消引用)。如果我在代码末尾像未注释的t.Stop()中那样停止它,则它将按预期工作。不知道为什么会这样。

答案 2 :(得分:-1)

time.AfterFunc(time.Second*5, func() {
        fmt.Println("AfterFunc")

        t := time.NewTicker(time.Second * 2)
        defer t.Stop()
        for range t.C {
            fmt.Println("Ticker")
        }
    })

它产生您需要的输出:

starting up
AfterFunc
Ticker
Ticker
Ticker
Ticker
Ticker