英文:
Repeating countdown with different duration
问题
我想制作一个具有两个不同持续时间的倒计时器。最好的方法是什么?我尝试这样做:
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
有什么惯用的方法可以实现这个?
英文:
I would like to make a countdown ticker with 2 different durations. What is the best way to do this? I try to do this:
s5 := time.Tick(5 * time.Second)
m5 := time.Tick(5 * time.Minute)
for {
select {
case t := <-s5:
...
case t := <-m5:
...
}
}
But I need ticker for different intervals:
5:00 -> 0:00
0:05 -> 0:00
5:00 -> 0:00
0:05 -> 0:00
Which is idiomatic way to do this?
答案1
得分: 3
你可以使用time.Sleep
来实现。
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)
...
}
...
}
或者你可以运行两个计时器,它们的间隔时间相差较小。
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)
}
}
你也可以尝试使用交替的Ticker
,但这需要更多的协调来延迟其中一个的启动。
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)
}
}
英文:
You could just call sleep if you want
dur := 1 * time.Second
nextDur := 3 * time.Second
for {
time.Sleep(dur)
dur, nextDur = nextDur, dur
...
}
Or alternate the durations in a time.Timer
if you need to select
. This is what I would personally stick with, since you don't need to worry about the offset between two timers skewing due to scheduling inconsistencies.
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)
...
}
...
}
Or run 2 timers offset by the smaller interval
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)
}
}
And you could also run interleaved Tickers like you originally tried, but that requires a little more coordination to delay the start of one of them
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)
}
}
答案2
得分: 1
一种解决方案是只有一个计时器,每5秒钟触发一次。5分钟加上5秒钟等于61*5秒钟。因此,“周期”是61个触发。每个第61个触发是5分钟标记,每个第61个触发后的一个触发是5秒钟标记。由于只有一个计时器,甚至不需要使用select
语句:
c, count := time.Tick(5*time.Second), 1
for {
<-c
count++
switch count % 61 {
case 0:
fmt.Println("5分钟标记")
case 1:
fmt.Println("5秒钟标记")
}
}
注意:由于count
初始化为1
,第一个“任务”将是在启动后5分钟执行的5分钟标记
。
另一种解决方案是使用两个time.Sleep()
调用的序列,第一个是5分钟,第二个是5秒钟:
for {
time.Sleep(5 * time.Minute)
fmt.Println("5分钟标记")
time.Sleep(5 * time.Second)
fmt.Println("5秒钟标记")
}
但是,这种方法的定时也取决于您执行的任务。因此,要么使用第一种解决方案,要么在单独的goroutine中执行任务,以免干扰定时,例如:
for {
time.Sleep(5 * time.Minute)
go func () {
fmt.Println("5分钟标记")
}()
time.Sleep(5 * time.Second)
go func () {
fmt.Println("5秒钟标记")
}()
}
英文:
One solution is to have only 1 ticker which ticks in every 5 seconds. 5 minutes plus 5 seconds is 61*5 seconds. So the "period" is 61 ticks. Every 61th
tick is the 5-minute mark, and every 61th+1
tick is a 5-sec mark. Since there is only one ticker, there is not even need for 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")
}
}
Note: since count
is initialized with 1
, the first "task" will be the 5-min mark
, executed after 5 min after start.
Another solution is to use a sequence of 2 time.Sleep()
calls, first being 5 min, second being 5 seconds:
for {
time.Sleep(5 * time.Minute)
fmt.Println("5-min mark")
time.Sleep(5 * time.Second)
fmt.Println("5-sec mark")
}
But timing of this also depends on the task you execute. So either use the first solution or execute the tasks in separate goroutines so they don't interfere with the timing, e.g.:
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")
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论