【问题标题】:Ticker should not execute if already running如果已经运行,则不应执行 Ticker
【发布时间】:2020-02-19 10:28:31
【问题描述】:

我有一些必须定期运行的功能。我为此使用了自动收报机。但是如果代码已经在运行,并且时间间隔再次过去,它不应该再次执行。

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(3*time.Second)
    flag := 0
    defer ticker.Stop()
    for {
        select {
        case t := <-ticker.C:
            flag = flag + 1
            if (flag % 2 ==0 ) {
                time.Sleep(time.Second*4)
            }   
            fmt.Println("Current time: ", t)
        }
    }
}

https://play.golang.org/p/2xV2MYInn4I

在操场上,自动收报机每 3 秒打印一次,但每次自动收报机的偶数转一圈,作业所花费的时间都比间隔时间长。我希望它不会运行并放弃那些滴答声。

我该怎么做?

【问题讨论】:

  • “我希望它不会运行并放弃那些滴答声。”你的期望是错误的。自动收报机间隔均匀,不知道您的代码做什么或不做什么。您可以自己“放弃”这个滴答声,也可以不使用 time.Ticker 但例如只是时间。考虑到您的任务花费了多长时间,您希望在下一个事件之前等待多长时间。
  • drop those ticks myself。为了执行此操作,我如何确定我的代码当前是否正在运行?
  • "如果我的代码当前正在运行" 你不能,这不是代码的工作方式,你的代码没有“运行”(嗯,它一直运行到程序结束)。您的 代码 正在运行或未运行。您不希望您的代码在其最后完成时间晚于下一个刻度时运行。因此,请记住您的代码上次完成的时间,将其与代码时间戳进行比较,然后重新运行您的代码。
  • 会尝试的。除了自动收报机之外,还有其他方法可以实现这一点吗?
  • 是的,决定下次何时运行代码,然后就睡那么久。

标签: go ticker


【解决方案1】:

睡在同一个 goroutine 中只会延迟执行。同时,ticker 在一个单独的 goroutine 中运行。因此,即使您使用全局变量来维持执行状态 - 它也不会为您提供所需的睡眠结果。然而,在一个单独的 goroutine 中迁移整个“睡眠”会产生:

package main

import (
    "fmt"
    "time"
)

type Tick struct {
    ticker *time.Ticker
    executing bool
}

func somethingYouWantToDo(tick *Tick, flag *int, t time.Time) {
    if tick.executing {
        return
    }

    tick.executing = true

    *flag = *flag + 1

    if (*flag % 2 ==0 ) {
                time.Sleep(time.Second*4)
    }   
        fmt.Println("Current time: ", t)
    tick.executing = false
}

func main() {
    tick := &Tick{
        ticker: time.NewTicker(3*time.Second),
    }
    flag := 0
    defer tick.ticker.Stop()
    for {
        select {
        case t := <-tick.ticker.C:
            go somethingYouWantToDo(tick, &flag, t)
        }
    }
}
// output
// Current time:  2009-11-10 23:00:03 +0000 UTC m=+3.000000001
// Current time:  2009-11-10 23:00:06 +0000 UTC m=+6.000000001
// Current time:  2009-11-10 23:00:12 +0000 UTC m=+12.000000001
// Current time:  2009-11-10 23:00:15 +0000 UTC m=+15.000000001
// Current time:  2009-11-10 23:00:21 +0000 UTC m=+21.000000001
// Current time:  2009-11-10 23:00:24 +0000 UTC m=+24.000000001
// Current time:  2009-11-10 23:00:30 +0000 UTC m=+30.000000001
// Current time:  2009-11-10 23:00:33 +0000 UTC m=+33.000000001
// Current time:  2009-11-10 23:00:39 +0000 UTC m=+39.000000001
// Current time:  2009-11-10 23:00:42 +0000 UTC m=+42.000000001
// Current time:  2009-11-10 23:00:48 +0000 UTC m=+48.000000001
// Current time:  2009-11-10 23:00:51 +0000 UTC m=+51.000000001
// Current time:  2009-11-10 23:00:57 +0000 UTC m=+57.000000001
// Current time:  2009-11-10 23:01:00 +0000 UTC m=+60.000000001
// Current time:  2009-11-10 23:01:06 +0000 UTC m=+66.000000001
// Current time:  2009-11-10 23:01:09 +0000 UTC m=+69.000000001

Try it on the playground

【讨论】:

  • 是的。终于自己弄明白了。类似的概念。 play.golang.org/p/g6qwQ8BcHDE
  • 请注意,您现在有一个数据竞争:第一个检查标志并清除它的 goroutine 正在与您启动的任何其他将检查标志并随后清除它的 goroutine 竞争。因为滴答声相距很远(秒),这场比赛不太可能引起问题,但它仍然存在。
  • 哪个会?我的-> play.golang.org/p/g6qwQ8BcHDE 或答案中的那个。就我而言,在任何时间点都只会运行一个 goroutine。
【解决方案2】:

股票通道是缓冲的,这就是为什么您可能会一个接一个地看到多个触发器。您可以通过简单地将代码的值传输到无缓冲通道来防止这种情况发生(另请注意,从代码接收到的 time.Time 值不是当前时间,而是最后一次滴答的时间):

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan time.Time) // unbuffered
    ticker := time.NewTicker(3 * time.Second)
    defer ticker.Stop()

    go func() {
        for t := range ticker.C {
            select {
            case c <- t:
            default:
            }
        }
    }()

    for flag := 0; flag < 8; flag++ {
        <-c

        if flag%2 == 0 {
            time.Sleep(time.Second * 4)
        }
        fmt.Println("Current time: ", time.Now())
    }
}

// Output:
// Current time:  2020-02-19 12:21:57.095433032 +0100 CET m=+3.000213350
// Current time:  2020-02-19 12:22:04.095585208 +0100 CET m=+10.000365520
// Current time:  2020-02-19 12:22:06.095363327 +0100 CET m=+12.000143680
// Current time:  2020-02-19 12:22:13.095605268 +0100 CET m=+19.000385598
// Current time:  2020-02-19 12:22:15.095371885 +0100 CET m=+21.000152174
// Current time:  2020-02-19 12:22:22.095537562 +0100 CET m=+28.000317857
// Current time:  2020-02-19 12:22:24.095431317 +0100 CET m=+30.000211625
// Current time:  2020-02-19 12:22:31.095524308 +0100 CET m=+37.000304595

在操场上试试看:https://play.golang.org/p/jDe5uJiRVe2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    • 2011-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多