Golang定时器在goroutine中阻塞

3

Below code is from 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

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

希望有人能帮忙,谢谢大家。

1个回答

4
问题可能是打印语句和程序结束之间没有"happens before"关系的保证。当主Goroutine退出时,整个程序也会退出。
Goroutines有一个启动时间,运行时有几个标准来切换到另一个正在运行的goroutine。可能发生的情况是,匿名函数中的代码从未被执行,因为主goroutine缺乏任何阻塞操作(甚至昂贵的函数调用),很快就退出了。
更改GOMAXPROCS,就像这个程序尝试做的那样,有时可以“修复”它,因为多个线程有机会偷偷地插入一些代码,因为它们不必依赖于运行时的显式上下文切换,但如果没有保证的“happens before”关系,或者至少没有一些声明来故意使主goroutine挂起一段时间(例如空的select{}for{}等),你不能指望在主goroutine之外的任何代码实际上都在运行。

完全有可能在另一台机器上(甚至是负载较小、超频等情况下的同一台机器上),您会得到您期望的行为。不幸的是,正如您所了解的,您不能依赖它,因此请确保同步您的goroutines。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接