Reputation: 2001
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?
Upvotes: 1
Views: 330
Reputation: 109318
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)
}
}
Upvotes: 3
Reputation: 417402
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")
}
}
Upvotes: 1