Golang中的定时器在goroutine中会阻塞。

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

golang timer blocking in goroutines

问题

以下代码来自go by example - timers

package main

import (
    "time"
    "fmt"
)

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    timer1 := time.NewTimer(time.Second * 1)

    <-timer1.C
    fmt.Println("Timer 1 expired")

    timer2 := time.NewTimer(300) //将持续时间更改为更短的时间
    go func() {
        <-timer2.C
        fmt.Printf("Timer 2 expired")
    }()

    stop2 := timer2.Stop()
    if stop2 {
        fmt.Printf("Timer 2 stopped")
    }
}

如果我运行上述代码,输出将会是这样(结果一):

Timer 1 expired
Timer 2 stopped

但是,如果我将匿名函数的主体更改为:

fmt.Printf("Timer 2 expired")
<-timer2.C

输出仍然与之前相同。我感到困惑,为什么第二个输出不是这样(结果二):

Timer 1 expired
Timer 2 expired
Timer 2 stopped

根据我的理解,<-timer2.C会阻塞协程的剩余部分,直到计时器通道接收到一个值,所以如果我将fmt.Printf("Timer 2 expired")放在<-timer2.C之后,输出将会像结果一样。但是,如果我将fmt.Printf("Timer 2 expired")放在<-timer2.C之前,我认为打印操作将不会被阻塞。

希望有人能帮助我,非常感谢。

英文:

Below code is from go by example - timers

package main

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

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    timer1 := time.NewTimer(time.Second * 1)

    &lt;-timer1.C
    fmt.Println(&quot;Timer 1 expired&quot;)

    timer2 := time.NewTimer(300) //change the duration to be more shorter
    go func() {
        &lt;-timer2.C
        fmt.Printf(&quot;Timer 2 expired&quot;)
    }()

    stop2 := timer2.Stop()
    if stop2 {
        fmt.Printf(&quot;Timer 2 stopped&quot;)
    }
}

If I run above code, the output will be like(result one):

Timer 1 expired
Timer 2 stopped

but if I change the body of the anonymous func to be:

fmt.Printf(&quot;Timer 2 expired&quot;)
&lt;-timer2.C

the output is still like before. I'm confused, why the second output is not like(result two):

Timer 1 expired
Timer 2 expired
Timer 2 stopped

As per my understanding <-timer2.C blocks the remain of goroutine until the timer channel get a value, so if I put fmt.Printf("Timer 2 expired") after <-timer2.C the output will like result one, but if I put fmt.Printf("Timer 2 expired") before <-timer2.C, I think the print action will not be blocked.

Hope someone could give me a hand, thank you all.

答案1

得分: 4

问题可能是由于打印语句和程序结束之间没有保证的“happens before”关系。当主 Goroutine 退出时,整个程序也会退出。

Goroutine 有一个启动时间,运行时有几个标准来确定何时切换到另一个正在运行的 Goroutine。可能发生的情况是,匿名函数中的代码根本没有被执行,因为主 Goroutine 很快退出,没有任何阻塞操作(甚至昂贵的函数调用)。

尝试更改 GOMAXPROCS 可能会“修复”这个问题,因为多个线程有机会在没有依赖于运行时的显式上下文切换的情况下运行一些代码,但是如果没有保证的“happens before”关系,或者至少没有一些故意使主 Goroutine 挂起一段时间的语句(例如空的 select{}for{} 等),你不能指望在主 Goroutine 之外的任何代码实际运行。

完全有可能在完全不同的机器上(甚至是负载较轻、超频等的同一台机器上)得到你期望的行为。不幸的是,正如你所了解的,你不能依赖它,所以确保同步你的 Goroutine。

英文:

The problem is likely that there's no guaranteed "happens before" relationship between the print statement and the end of the program. When the main Goroutine exits, the whole program exits.

Goroutines have a startup time, and the runtime has several criteria for when it switches to another running goroutine. What's probably happening is that no code in the anonymous function is ever being executed, because the main goroutine, lacking any blocking operations (or even expensive function calls), exits very quickly

Changing GOMAXPROCS, as this program attempts to do, can sometimes "fix" that, in that the multiple threads have a chance of sneaking in some code because they don't have to rely on an explicit context switch from the runtime, but without a guaranteed "happens before" relationship, or at least without some statement to intentionally make the main goroutine hang for a while (e.g. the empty select{}, for{} etc) you can't rely on any code outside the main goroutine ever actually running.

It's entirely possible that on a completely different machine (or even the same machine with less load, or overclocked or...), you'd get the behavior that you expect. Unfortunately since, as you learned, you can't count on it, make sure you synchronize your goroutines.

huangapple
  • 本文由 发表于 2014年5月30日 13:38:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/23947770.html
匿名

发表评论

匿名网友

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

确定