Golang计时器的长度为0

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

Golang Timers with 0 length

问题

我已经翻译了你的代码部分,以下是翻译好的内容:

我写了一段代码片段,创建了一个长度为0的计时器,并且它不会立即过期(这是我预期的)。一个非常短的睡眠调用确实会使它过期,但我对此感到困惑。

我关心的原因是,使用这个想法的代码片段在低概率错误上返回0,想法是计时器应该立即过期,并重试一个函数。我不认为这里需要的纳秒级睡眠会影响我的实现,但这让我感到困扰。

我是否犯了一个错误,这是预期的行为吗?

谢谢!

package main

import (
	"fmt"
	"time"
)

func main() {
	testTimer := time.NewTimer(time.Duration(0) * time.Millisecond)
	fmt.Println(Expired(testTimer))
	time.Sleep(time.Nanosecond)
	fmt.Println(Expired(testTimer))
}

func Expired(T *time.Timer) bool {
	select {
	case <-T.C:
		return true
	default:
		return false
	}
}

打印结果:

false
true

Playground链接:https://play.golang.org/p/xLLHoR8aKq

英文:

I've written a code snipped that creates a timer with a 0 length time, and it does not immediately expire (which is what I expected). A very short sleep call does make it expire, but I'm confused as to why.

The reason I care is that the code using this idea has a snippet that returns 0 on a low probability error, with the idea that the timer should be set to immediately expire, and retry a function. I do not believe that the nanosecond sleep needed here will affect my implementation, but it bothers me.

Did I make a mistake, is this expected behaviour?

Thanks!

 package main
    
    import (
    	&quot;fmt&quot;
    	&quot;time&quot;
    )
    
    func main() {
    	testTimer := time.NewTimer(time.Duration(0) * time.Millisecond)
    	fmt.Println(Expired(testTimer))
    	time.Sleep(time.Nanosecond)
    	fmt.Println(Expired(testTimer))
    }
    
    func Expired(T *time.Timer) bool {
    	select {
    	case &lt;-T.C:
    		return true
    	default:
    		return false
    	}
    }

Playground link: https://play.golang.org/p/xLLHoR8aKq

Prints

false
true

答案1

得分: 7

time.NewTimer()不能保证最大等待时间,它只能保证最小等待时间。引用它的文档:

> NewTimer 创建一个新的计时器,在至少经过时间段 d 之后,向其通道发送当前时间。

因此,如果将零时段传递给 time.NewTimer(),返回的 time.Timer 不会立即“过期”是不奇怪的。

如果实现检查传递的时段是否为零,并在返回计时器之前在计时器的通道上发送一个值,那么返回的计时器可能会立即“过期”,但实际上并没有这样做。相反,它像对于任何给定的时段一样正常启动一个内部计时器,该计时器将负责在将来的某个时间发送一个值到它的通道上。

请注意,如果有多个 CPU 核心,并且 runtime.GOMAXPROCS() 大于 1,那么在 NewTimer() 返回之前,另一个 goroutine(time 包内部的)有一定机会在计时器的通道上发送一个值,但这个机会非常小...另外,由于这是实现细节,将来的版本可能会添加这个“优化”来检查传递的时段是否为零,并按照您的预期进行操作,但是像所有的实现细节一样,不要依赖它。依赖文档中所描述的,并且不要期望更多。

英文:

time.NewTimer() does not guarantee maximum wait time. It only guarantees a minimum wait time. Quoting from its doc:

> NewTimer creates a new Timer that will send the current time on its channel after at least duration d.

So passing a zero duration to time.NewTimer(), it's not a surprise the returned time.Timer is not "expired" immediately.

The returned timer could be "expired" immediately if the implementation would check if the passed duration is zero, and would send a value on the timer's channel before returning it, but it does not. Instead it starts an internal timer normally as it does for any given duration, which will take care of sending a value on its channel, but only some time in the future.

Note that with multiple CPU cores and with runtime.GOMAXPROCS() being greater than 1 there is a slight chance that another goroutine (internal to the time package) sends a value on the timer's channel before NewTimer() returns, but this is a very small chance... Also since this is implementation detail, a future version might add this "optimization" to check for 0 passed duration, and act as you expected it, but as with all implementation details, don't count on it. Count on what's documented, and expect no more.

答案2

得分: 2

Go的计时器函数保证至少休眠指定的时间。分别查看SleepNewTimer的文档:

Sleep暂停当前的goroutine,至少持续时间d。负数或零的持续时间会导致Sleep立即返回。

NewTimer创建一个新的计时器,在至少持续时间d之后,将当前时间发送到其通道。

(已添加强调)

在你的情况下,如果你不想完全休眠,你可能应该不使用计时器。

英文:

Go's timer functions guarantee to sleep at least the specified time. See the docs for Sleep and NewTimer respectively:

> Sleep pauses the current goroutine for at least the duration d. A negative or zero duration causes Sleep to return immediately.
>
> NewTimer creates a new Timer that will send the current time on its channel after at least duration d.
>
> (emphasis added)

In your situation, you should probably just not use a timer in the situation that you don't want to sleep at all.

答案3

得分: 0

这是由于设置计时器对象所需的内部时间。如果你注意到下面的 playground 链接中,计时器确实在正确的时间到期,但是设置和启动计时器的内部 go routine 比你的 Expire 函数检查它所花费的时间更长。

> 当计时器到期时,当前时间将发送到 C(通道)

因此,你会注意到在计时器到期后,它仍然发送原始时间,因为它在纳秒级别的 Sleep 完成之前就已经到期了。

https://play.golang.org/p/Ghwq9kJq3J

package main

import (
	"fmt"
	"time"
)

func main() {
	testTimer := time.NewTimer(0 * time.Millisecond)
	Expired(testTimer)
	time.Sleep(time.Nanosecond)
	Expired(testTimer)
	n := time.Now()
	fmt.Printf("等待后:%d\n", n.UnixNano())

}

func Expired(T *time.Timer) bool {
	select {
	case t:= <-T.C:
		fmt.Printf("到期:%d\n", t.UnixNano())
		return true
	default:
		n := time.Now()
		fmt.Printf("未到期:%d\n", n.UnixNano())
		return false
	}
}
英文:

This is due to the internal time it takes to set up the timer object. If you'll note in the playground link below the timer does expire at the proper time, but the internal go routine that sets it up and starts it takes longer than your Expire function does to check it.

> When the Timer expires, the current time will be sent on C (the channel)

So you'll notice that after it expires, it still sends the original time, because it has expired even before the nanosecond Sleep finished.

https://play.golang.org/p/Ghwq9kJq3J

package main

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

func main() {
	testTimer := time.NewTimer(0 * time.Millisecond)
	Expired(testTimer)
	time.Sleep(time.Nanosecond)
	Expired(testTimer)
	n := time.Now()
	fmt.Printf(&quot;after waiting: %d\n&quot;, n.UnixNano())

}

func Expired(T *time.Timer) bool {
	select {
	case t:= &lt;-T.C:
		fmt.Printf(&quot;expired %d\n&quot;, t.UnixNano())
		return true
	default:
		n := time.Now()
		fmt.Printf(&quot;not expired: %d\n&quot;, n.UnixNano())
		return false
	}
}

huangapple
  • 本文由 发表于 2017年4月26日 00:32:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/43616295.html
匿名

发表评论

匿名网友

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

确定