【问题标题】:Why does time.After never fire when it is paired with a ticker in a select block?为什么 time.After 与选择块中的代码配对时永远不会触发?
【发布时间】:2016-06-08 01:26:35
【问题描述】:

我有一个选择块正在监听 2 个频道、一个自动收报机和一个计时器:

package main

import (
    "fmt"
    "time"
)

func main() {

    ticker := time.NewTicker(5 * time.Second)
    for {
        select {
        case z := <-ticker.C:
            fmt.Printf("tick %d\n", z)

        case <-time.After(12 * time.Second):
            fmt.Println("12 seconds elapsed!")
        }
    }
}

如果我运行代码,time.After 案例永远不会运行,但代码可以正常工作。

如果我删除代码,time.After 会正确触发:

package main

import (
    "fmt"
    "time"
)

func main() {

    for {
        select {
        case <-time.After(12 * time.Second):
            fmt.Println("12 seconds elapsed!")
        }
    }
}

如果我使用计时器而不是time.After,它可以正常工作:

package main

import (
    "fmt"
    "time"
)

func main() {

    ticker := time.NewTicker(5 * time.Second)
    timer := time.NewTimer(12 * time.Second)
    for {
        select {
        case z := <-ticker.C:
            fmt.Printf("tick %d\n", z)

        case <-timer.C:
            fmt.Println("12 seconds elapsed!")
        }
    }
}

为什么会这样?

【问题讨论】:

  • 我不明白这个问题。 12 秒总是比 5 秒长;您能解释一下您对 time.After 在不到 5 秒内触发的原因吗?
  • 我预计它不会在 5 秒内触发。我预计在 5 秒时会有一个滴答声,在 10 秒和 12 秒时会有另一个滴答声,time.After 应该会触发。但是,除非我使用 time.NewTimer 创建计时器,否则无论我等待多长时间,time.After 都不会触发。

标签: go timer ticker


【解决方案1】:

A select 阻塞,直到它的一个案例准备好,然后它执行那个案例。在您的示例中 time.After() 永远不会被调用。

func main() {

    ticker := time.NewTicker(5 * time.Second)
    for {
        select {
        case z := <-ticker.C:
            fmt.Printf("tick %d\n", z)

        //This never gets chance to be ready. It'll work if you make it less than 5 seconds.
        case <-time.After(12 * time.Second): 
            fmt.Println("12 seconds elapsed!")
        }
    }
}

您可以通过在for 循环之前声明计时器来使其工作。

func main() {

        ticker := time.NewTicker(5 * time.Second)
        timer := time.After(12 * time.Second)
        for {
                select {
                case z := <-ticker.C:
                        fmt.Printf("tick %d\n", z)

                case <-timer:
                        fmt.Println("12 seconds elapsed!")
                }   
        }   
}   

【讨论】:

    【解决方案2】:

    关键是,当 select 被调用时,它会在 all case 子句中重新创建通道。如果您在case &lt;- newCreateTimerChannel 中创建计时器,它将启动一个新计时器。所以把定时器的创建放在for循环之外,让它成为一个全局定时器。

    【讨论】:

      猜你喜欢
      • 2014-09-15
      • 2016-02-10
      • 1970-01-01
      • 1970-01-01
      • 2018-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-09
      相关资源
      最近更新 更多