在goroutines中阻止golang计时器

时间:2014-05-30 05:38:30

标签: timer go blocking goroutine

以下代码来自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) //change the duration to be more shorter
    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

按照我的理解&lt; -timer2.C 阻止goroutine的剩余,直到定时器通道获得一个值,所以如果我把 fmt.Printf(&#34;定时器2)在&lt; -timer2.C 之后过期&#34;)输出会像结果一样,但如果我把 fmt.Printf(&#34;定时器2过期&#34) ;)&lt; -timer2.C 之前的,我认为不会阻止打印操作。

希望有人能帮助我,谢谢大家。

1 个答案:

答案 0 :(得分:4)

问题可能是print语句和程序结束之间没有保证"happens before"的关系。当主要的Goroutine退出时,整个程序退出。

Goroutines有一个启动时间,运行时有几个标准可用于何时切换到另一个正在运行的goroutine。可能发生的事情是匿名函数中没有代码被执行,因为主要的goroutine缺少任何阻塞操作(甚至是昂贵的函数调用),退出非常快

正如此程序尝试的那样,更改GOMAXPROCS有时可以“修复”,因为多个线程有一些机会潜入某些代码,因为它们没有依赖于来自运行时的显式上下文切换,但没有保证“发生在之前”关系,或者在至少没有一些语句来故意使主goroutine挂起一段时间(例如空{{ 1}},select{}等)你不能依赖实际运行的主goroutine之外的任何代码。

完全可能在完全不同的机器上(甚至是负载较小或超频或相同的机器),您将获得您期望的行为。不幸的是,因为你了解到,你不能指望它,确保你同步你的goroutines。