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

huangapple go评论153阅读模式
英文:

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 := &lt;-s5:
            ...
        case t := &lt;-m5:
            ...
    }
}

But I need ticker for different intervals:

5:00 -&gt; 0:00
0:05 -&gt; 0:00
5:00 -&gt; 0:00
0:05 -&gt; 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 := &lt;-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 := &lt;-timer1.C:
		timer1.Reset(dur1 + dur2)
		fmt.Println(&quot;timer1:&quot;, t)
	case t := &lt;-timer2.C:
		timer2.Reset(dur1 + dur2)
		fmt.Println(&quot;timer2:&quot;, 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 := &lt;-ticker1.C:
		once.Do(delayOnce)
		fmt.Println(&quot;ticker1:&quot;, t)
	case t := &lt;-ticker2.C:
		fmt.Println(&quot;ticker2:&quot;, 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 {
	&lt;-c
	count++
	switch count % 61 {
	case 0:
		fmt.Println(&quot;5-min mark&quot;)
	case 1:
		fmt.Println(&quot;5-sec mark&quot;)
	}
}

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(&quot;5-min mark&quot;)
	time.Sleep(5 * time.Second)
	fmt.Println(&quot;5-sec mark&quot;)
}

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(&quot;5-min mark&quot;)
	}
	time.Sleep(5 * time.Second)
	go func () {
		fmt.Println(&quot;5-sec mark&quot;)
	}
}

huangapple
  • 本文由 发表于 2016年1月28日 23:46:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/35065928.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定