英文:
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
}
但是,我对自己的做法不太满意。我不应该进行计数,对吗?我应该进行倒计时...我只是感到困惑,需要帮助确定应该使用哪种方法。
我希望发生以下情况:
- 当
certainSomething
发生时,从timeDelay
开始倒计时到0。 - 在倒计时达到0之前,不要调用
iShouldOnlyBeCalledWhenDelayHasBeenReached
。 - 所有这些都应该发生在一个循环内,具体来说是一个接收数据包的服务器循环。
我的问题是:
- 为了实现这种倒计时风格,我应该怎么做?
谢谢,任何建议或示例代码都会帮助很多。
注意:循环中还有其他函数。在做其他事情。这是主循环。我不能使用
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 < timeDelay) { // lessThan()
// still has not reached target count (time elapsed)
fmt.Println("Not Yet")
// we dont want to get to the end of the loop yet
continue
}
if (timeTick > timeDelay) { // greaterThan()
// has finally reached the target count (time elapsed)
fmt.Println("Yes! Finally")
// 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'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 < time.Duration(upper)*time.Microsecond && diff != 0
}
func greaterThan(diff time.Duration, upper int) bool {
return diff > 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:
- A countdown from
timeDelay
to 0 starts whencertainSomething
happens. - Do not call
iShouldOnlyBeCalledWhenDelayHasBeenReached
until the countdown hits 0. - This should all happen inside a loop, a server loop receiving packets to be exact.
My question:
- 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 (
"fmt"
"time"
)
func main() {
certainSomething := true // will cause time loop to repeat
timeDelay := 900 * time.Millisecond // == 900000 * time.Microsecond
var endTime <-chan time.Time // signal for when timer us up
for {
// if a certain something happens, start a timer
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) // simulate work
continue
}
// function shouldn't be called if the elapsed amount
// of time required has not yet been reached
iShouldOnlyBeCalledWhenDelayHasBeenReached() // this could also just be moved to the <- endtime block above
}
}
func iShouldOnlyBeCalledWhenDelayHasBeenReached() {
fmt.Println("I've been called")
}
答案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't block
// and we can check the state in the select.
go func(){
// complicated logic here!
//
// you must issue an "I am done!"
done <- 1
}()
// this will block the for loop until a timeout occurs
select {
case <- done:
continue
case <- time.After(1000 * time.Nanosecond):
iShouldOnlyBeCalledWhenDelayHasBeenReached()
}
}
}
答案3
得分: 1
如果你无法让例程休眠,你可以使用time.Time.Add
和time.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 "time"
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 "time"
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("时间到了!")
}
英文:
I think this will solve your problem.
package main
import (
"fmt"
"time"
)
func main() {
time.Sleep(2 * time.Second)
fmt.Println("Time is up!")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论