重复不同持续时间的倒计时

Repeating countdown with different duration

我想制作一个具有 2 个不同持续时间的倒计时代码。做这个的最好方式是什么?我尝试这样做:

s5 := time.Tick(5 * time.Second)
m5 := time.Tick(5 * time.Minute)

for {
    select {
        case t := <-s5:
            ...
        case t := <-m5:
            ...
    }
}

但我需要不同时间间隔的代码:

5:00 -> 0:00
0:05 -> 0:00
5:00 -> 0:00
0:05 -> 0:00

执行此操作的惯用方法是什么?

一种解决方案是只设置 1 个每 5 秒滴答一次的自动收报机。 5 分加 5 秒等于 61*5 秒。所以 "period" 是 61 个刻度。每 61th 个刻度为 5 分钟标记,每个 61th+1 个刻度为 5 秒标记。由于只有一个自动收报机,甚至不需要 select:

c, count := time.Tick(5*time.Second), 1
for {
    <-c
    count++
    switch count % 61 {
    case 0:
        fmt.Println("5-min mark")
    case 1:
        fmt.Println("5-sec mark")
    }
}

注意:由于 count 是用 1 初始化的,第一个 "task" 将是 5-min mark,在启动后 5 分钟后执行。

另一个解决方案是使用一系列 2 time.Sleep() 调用,第一个是 5 分钟,第二个是 5 秒:

for {
    time.Sleep(5 * time.Minute)
    fmt.Println("5-min mark")
    time.Sleep(5 * time.Second)
    fmt.Println("5-sec mark")
}

但是这个时间也取决于你执行的任务。因此,要么使用第一个解决方案,要么在单独的 goroutine 中执行任务,这样它们就不会干扰时间,例如:

for {
    time.Sleep(5 * time.Minute)
    go func () {
        fmt.Println("5-min mark")
    }
    time.Sleep(5 * time.Second)
    go func () {
        fmt.Println("5-sec mark")
    }
}

如果你愿意,你可以直接调用睡眠

dur := 1 * time.Second
nextDur := 3 * time.Second

for {
    time.Sleep(dur)
    dur, nextDur = nextDur, dur

    ...
}

如果您需要 select,或者在 time.Timer 中替换持续时间。这是我个人坚持的做法,因为您不必担心由于调度不一致而导致两个定时器之间的偏差偏移。

dur := 1 * time.Second
nextDur := 3 * time.Second

timer := time.NewTimer(dur)

for {
    select {
    case t := <-timer.C:
        dur, nextDur = nextDur, dur
        timer.Reset(dur)
        ...
    }
    ...
}

或运行 2个计时器偏移较小的间隔

dur1 := 1 * time.Second
dur2 := 3 * time.Second

timer1 := time.NewTimer(dur1)
timer2 := time.NewTimer(dur1 + dur2)

for {
    select {
    case t := <-timer1.C:
        timer1.Reset(dur1 + dur2)
        fmt.Println("timer1:", t)
    case t := <-timer2.C:
        timer2.Reset(dur1 + dur2)
        fmt.Println("timer2:", t)
    }

}

您也可以像您最初尝试的那样 运行 交错代码,但这需要更多的协调才能延迟其中一个代码的启动

dur1 := 1 * time.Second
dur2 := 3 * time.Second

ticker1 := time.NewTicker(dur1)
ticker2 := time.NewTicker(dur1 + dur2)

var once sync.Once
delayOnce := func() {
    ticker1.Stop()
    ticker1 = time.NewTicker(dur1 + dur2)
}

for  {
    select {
    case t := <-ticker1.C:
        once.Do(delayOnce)
        fmt.Println("ticker1:", t)
    case t := <-ticker2.C:
        fmt.Println("ticker2:", t)
    }

}