在循环中进行倒计时的Go计时器

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

Go countdown timer in loop

问题

我目前有这段代码,它试图计算经过的时间以触发某个条件。(伪代码):

timeDelay = 900000 // time.Microsecond
for {
   // 如果某个特定事件发生,开始计时(时间)
   if (certainSomething) {
      startTime = time.Now();

      if !prevTime.IsZero() {
        // 将经过的时间添加到timeTick
        diffTime = time.Since(prevTime)
        timeTick = timeTick + diffTime
      }

      prevTime = startTime
   }

   if (timeTick < timeDelay) { // lessThan()
      // 还没有达到目标计数(经过的时间)
      fmt.Println("Not Yet")
      // 我们不想立即结束循环
      continue
   }

   if (timeTick > timeDelay) { // greaterThan()
      // 终于达到了目标计数(经过的时间)
      fmt.Println("Yes! Finally")
      // 是的,延迟已经达到,让我们重置时间
      // 如果`certainSomething`再次触发,让我们重新开始计时
      timeTick = time.Duration(0 * time.Microsecond)
   }

   // 只有在达到所需的经过时间时才应调用该函数
   iShouldOnlyBeCalledWhenDelayHasBeenReached();
}

我还使用以下作为辅助函数(实际代码):

func lessThan(diff time.Duration, upper int) bool {
	return diff < time.Duration(upper)*time.Microsecond && diff != 0
}

func greaterThan(diff time.Duration, upper int) bool {
	return diff > time.Duration(upper)*time.Microsecond
}

但是,我对自己的做法不太满意。我不应该进行计数,对吗?我应该进行倒计时...我只是感到困惑,需要帮助确定应该使用哪种方法。

我希望发生以下情况:

  1. certainSomething发生时,从timeDelay开始倒计时到0。
  2. 在倒计时达到0之前,不要调用iShouldOnlyBeCalledWhenDelayHasBeenReached
  3. 所有这些都应该发生在一个循环内,具体来说是一个接收数据包的服务器循环。

我的问题是:

  1. 为了实现这种倒计时风格,我应该怎么做?

谢谢,任何建议或示例代码都会帮助很多。

注意:循环中还有其他函数。在做其他事情。这是主循环。我不能使用Sleep

英文:

I currently have this code that is trying to count the elapsed time to trigger a certain condition. (pseudo):

timeDelay = 900000 // time.Microsecond
for {
   // if a certain something happens, start a counting (time)
   if (certainSomething) {
      startTime = time.Now();

      if !prevTime.IsZero() {
        // add the time elapsed time to timeTick
        diffTime = time.Since(prevTime)
        timeTick = timeTick + diffTime
      }

      prevTime = startTime
   }

   if (timeTick &lt; timeDelay) { // lessThan()
      // still has not reached target count (time elapsed)
      fmt.Println(&quot;Not Yet&quot;)
      // we dont want to get to the end of the loop yet
      continue
   }

   if (timeTick &gt; timeDelay) { // greaterThan()
      // has finally reached the target count (time elapsed)
      fmt.Println(&quot;Yes! Finally&quot;)
      // yes the delay has been reached lets reset the time
      // and if `thisHappened` is triggered again, lets start counting again
      timeTick = time.Duration(0 * time.Microsecond)
   }

   // function shouldn&#39;t be called if the elapsed amount
   //      of time required has not yet been reached
   iShouldOnlyBeCalledWhenDelayHasBeenReached();
}

I'm also using these as helper functions (actual code)

func lessThan(diff time.Duration, upper int) bool {
	return diff &lt; time.Duration(upper)*time.Microsecond &amp;&amp; diff != 0
}

func greaterThan(diff time.Duration, upper int) bool {
	return diff &gt; time.Duration(upper)*time.Microsecond
}

But, I'm just not comfortable of how I'm doing it. I shouldn't be counting up, right? I should be counting down... I'm just confused and need help on what approach I should use.

What I want to happen:

  1. A countdown from timeDelay to 0 starts when certainSomething happens.
  2. Do not call iShouldOnlyBeCalledWhenDelayHasBeenReached until the countdown hits 0.
  3. This should all happen inside a loop, a server loop receiving packets to be exact.

My question:

  1. What should I do to achieve that countdown style?

Thank you, any suggestion or example code would help a lot.

> Note: There are other functions in the loop. Doing other things. This is the main loop. I can't make it Sleep.

答案1

得分: 2

你可以设置一个通道,用来在超过时间时通知你。

这里有一个示例:

它的一个附加好处是,在select语句中,你可以为其他目的设置其他通道。例如,如果你在这个循环中做其他工作,你也可以在一个goroutine中启动该工作,并在另一个通道上发送结果。然后,在timeDelay结束或其他工作完成时退出。

package main

import (
	"fmt"
	"time"
)

func main() {
	certainSomething := true // 会导致时间循环重复

	timeDelay := 900 * time.Millisecond // == 900000 * time.Microsecond

	var endTime <-chan time.Time // 当计时器结束时的信号

	for {
		// 如果某个特定事件发生,启动计时器
		if certainSomething && endTime == nil {
			endTime = time.After(timeDelay)
		}
		select {
		case <-endTime:
			fmt.Println("Yes Finally!")
			endTime = nil
		default:
			fmt.Println("not yet")
			time.Sleep(50 * time.Millisecond) // 模拟工作
			continue
		}

		// 只有在达到所需的经过时间时才应调用该函数
		iShouldOnlyBeCalledWhenDelayHasBeenReached() // 这也可以直接移动到上面的<-endtime块中
	}
}

func iShouldOnlyBeCalledWhenDelayHasBeenReached() {
	fmt.Println("I've been called")
}

示例链接

英文:

You can setup a channel to let you know when you've exceeded the time.

Here's an example on play

It has an added benefit that in the select statement, you can have other channels for other purposes. For example,if you are doing other work in this loop, you could also spawn that work in a goroutine and have it send the result back on another channel.
Then you exit after timeDelay or when other work completes, or whatever.

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func main() {
	certainSomething := true // will cause time loop to repeat

	timeDelay := 900 * time.Millisecond // == 900000 * time.Microsecond

	var endTime &lt;-chan time.Time // signal for when timer us up

	for {
		// if a certain something happens, start a timer
		if certainSomething &amp;&amp; endTime == nil {
			endTime = time.After(timeDelay)
		}
		select {
		case &lt;-endTime:
			fmt.Println(&quot;Yes Finally!&quot;)
			endTime = nil
		default:
			fmt.Println(&quot;not yet&quot;)
			time.Sleep(50 * time.Millisecond) // simulate work
			continue
		}

		// function shouldn&#39;t be called if the elapsed amount
		//      of time required has not yet been reached
		iShouldOnlyBeCalledWhenDelayHasBeenReached() // this could also just be moved to the &lt;- endtime block above
	}
}

func iShouldOnlyBeCalledWhenDelayHasBeenReached() {
	fmt.Println(&quot;I&#39;ve been called&quot;)
}

答案2

得分: 2

由于这是一个游戏,根据你的说法,我理解你的需求是:

  • 想要完成一定数量的工作
  • 如果工作时间超过了delay的时间,停止工作并重新开始循环

如果是这种情况,你有几种选择和模式。

我想到了time.After

我喜欢在select语句中使用time.After的简洁性。不需要使用通道或goroutine来处理它。

这种模式还有一个额外的好处,就是可以使用goroutine来处理主要的游戏逻辑。

在play链接中可以查看示例代码:http://play.golang.org/p/FIiUJ0CHZz

更改time.After()的参数可以看到它的效果。

func main() {
    
    for {
    
        // 在每次循环中创建一个新的通道,这样GC可以在超时后回收任何完成的游戏内容。
        done := make(chan int)
        
        // 主要的游戏函数,处理其他的工作。
        // 由于这是在goroutine中,它不会阻塞
        // 并且我们可以在select中检查状态。
        go func(){
        
            // 复杂的逻辑在这里!
            //
            
            // 你必须发送一个“我完成了!”的信号
            done <- 1
        }()

        // 这将阻塞for循环,直到超时发生
        select {
            case <- done:
                continue
            case <- time.After(1000 * time.Nanosecond):
                iShouldOnlyBeCalledWhenDelayHasBeenReached()
        }
    }
}
英文:

Since this is a game as you say, it sounds to me that you:

  • want to do X amount of work
  • if the time of work exceeds delay amount, stop work and loop again

If this is the case, then you have a few options and patterns.

time.After come to mind.

I like the cleanliness of time.After in a select statement. No channels or goroutines needed to handle it.

This pattern also has the added benefit of using goroutines for your main game logic.

On play: http://play.golang.org/p/FIiUJ0CHZz

Change the time.After() to see it in action.

func main() {
    
	for {
	
		// creating a new channel on each loop will allow the
		// GC to collect any game stuff that completes after the timeout.
		done := make(chan int)
		
		// main game func, doing your other stuff.
		// since this is in a goroutine, it won&#39;t block
		// and we can check the state in the select.
		go func(){
		
			// complicated logic here!
			//
			
			// you must issue an &quot;I am done!&quot;
			done &lt;- 1
		}()

		// this will block the for loop until a timeout occurs
		select {
			case &lt;- done:
				continue
			case &lt;- time.After(1000 * time.Nanosecond):
				iShouldOnlyBeCalledWhenDelayHasBeenReached()
		}
	}
}

答案3

得分: 1

如果你无法让例程休眠,你可以使用time.Time.Addtime.Time.After(或time.Time.Before)函数。

以下是类似的代码示例:

package main

import "time"

func main() {

    var d = 1000 * time.Microsecond
    var t = time.Now().Add(d)

    for {
        if time.Now().Before(t) {
            continue
        }

        do_something()
    }
}

或者,将需要在一定时间后运行的方法放入一个可以休眠的例程中:

package main

import "time"

func main() {

    var d = 1000 * time.Microsecond

    go func() {
        time.Sleep(d)
        do_something()
    }()
}

以上是翻译好的内容,请确认是否满意。

英文:

If you can't make the routine sleep, you should use the time.Time.Add and time.Time.After (or time.Time.Before) functions.

Something along theses lines

package main         
                     
import &quot;time&quot;        
                     
func main() {        
                     
    var d = 1000 * time.Microsecond
    var t = time.Now().Add(d)
                     
    for {            
        if time.Now().Before(t) {
            continue 
        }            
                     
        do_something()
    }                
}

Or deport the method you have to run after a certain time in a routine that you can make sleep:

package main         
                     
import &quot;time&quot;        
                     
func main() {

    var d = 1000 * time.Microsecond

    go func() {
        time.Sleep(d)
        do_something()
    }()
}

答案4

得分: 0

我认为这个代码可以解决你的问题。

package main

import (
	"fmt"
	"time"
)

func main() {
	time.Sleep(2 * time.Second)
	fmt.Println("时间到了!")
}

Playground

英文:

I think this will solve your problem.

package main

import (
    &quot;fmt&quot;
    &quot;time&quot;
)

func main() {
    time.Sleep(2 * time.Second)
    fmt.Println(&quot;Time is up!&quot;)
}

<kbd>Playground</kbd>

huangapple
  • 本文由 发表于 2016年3月17日 20:58:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/36061263.html
匿名

发表评论

匿名网友

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

确定