Golang计时器,长度为0

时间:2017-04-25 16:32:39

标签: go timer

我写了一个代码剪切,创建一个长度为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
        }
    }

游乐场链接:https://play.golang.org/p/xLLHoR8aKq

打印

false
true

3 个答案:

答案 0 :(得分:5)

time.NewTimer()不保证最长等待时间。它只保证最小等待时间。引用其文档:

  

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

所以将零持续时间传递给time.NewTimer(),返回的time.Timer不会立即“过期”并不奇怪。

如果实现将检查传递的持续时间是否为零,则返回的计时器可以立即“过期”,并且在返回之前会在计时器的通道上发送一个值,但事实并非如此。相反,它正常地启动一个内部计时器,就像它在任何给定的持续时间一样,它将负责在其通道上发送一个值,但仅在将来的某个时间。

请注意,对于多个CPU内核且runtime.GOMAXPROCS()大于1的情况,另一个goroutine(time包内部)在{{1}之前在定时器通道上发送值的可能性很小。返回,但这是一个非常小的机会......此外,由于这是实现细节,未来版本可能会添加此“优化”以检查0传递的持续时间,并按预期执行,但与所有实现细节一样,不要指望它。依靠记录的内容,不再期待。

答案 1 :(得分:1)

Go的计时器功能保证至少指定指定的时间。分别参阅SleepNewTimer的文档:

  

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

     

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

     

(强调补充)

在你的情况下,你根本不应该在你根本不想睡觉的情况下使用计时器。

答案 2 :(得分:0)

这是由于设置计时器对象所需的内部时间。如果您在下面的游乐场链接中注意到计时器在适当的时间到期,但是设置它并启动它的内部例行程序需要比Sleep函数更长的时间来检查它。

  

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

因此,您会注意到它过期后仍会发送原始时间,因为它已经在纳秒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("after waiting: %d\n", n.UnixNano()) } func Expired(T *time.Timer) bool { select { case t:= <-T.C: fmt.Printf("expired %d\n", t.UnixNano()) return true default: n := time.Now() fmt.Printf("not expired: %d\n", n.UnixNano()) return false } } 完成之前就已过期。

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

function urlIsPublic($ip_list) {
    $ips = explode(',', $ip_list);
    foreach($ips as $ip) {
        $ip = trim($ip);
        if(filter_var($ip,
                FILTER_VALIDATE_IP,
                FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)
            !== false) {
            return $ip;
        }
    }
    return false;
}