一尘不染

Go Playground和我的机器上的Go之间有差异吗?

go

为了解决我对goroutine的一些误解,我去了Go游乐场并运行了以下代码

package main

import (
    "fmt"
)

func other(done chan bool) {
    done <- true
    go func() {
        for {
            fmt.Println("Here")
        }
    }()
}

func main() {
    fmt.Println("Hello, playground")
    done := make(chan bool)
    go other(done)
    <-done
    fmt.Println("Finished.")
}

如我所料,Go操场出现了一个错误: 过程花费了太长时间

这似乎暗示在其中创建的goroutine other永远运行。

但是,当我在自己的机器上运行相同的代码时,几乎立即获得以下输出:

Hello, playground.
Finished.

这似乎暗示other主goroutine完成时,其中的goroutine将退出。 这是真的?
还是主goroutine完成,而其他goroutine继续在后台运行?


阅读 262

收藏
2020-07-02

共1个答案

一尘不染

您看到的解释:

在Go
Playground上,GOMAXPROCS1证明)。

这意味着一次执行一个goroutine,并且如果该goroutine没有阻塞,则不会强制调度程序切换到其他goroutine。

您的代码(像每个Go应用程序一样)以执行main()函数的goroutine(主goroutine)开始。它启动另一个执行该other()功能的goroutine
,然后从done通道接收-阻塞。因此,调度程序必须切换到另一个goroutine(执行other()功能)。

other()函数中,当您在done通道上发送值时,这将使当前(other())和maingoroutine可运行。调度选择继续运行other(),并且因为GOMAXPROCS=1main()没有继续。现在other()启动另一个执行无限循环的goroutine。调度程序选择执行此goroutine,这将永远花费到阻塞状态,因此main()不会继续。

然后,Go Playground沙箱的超时就可以解决:

过程花了太长时间

请注意, Go Memory Model
仅保证某些事件先于其他事件发生,而不能保证如何执行2个并发goroutine。这使得输出不确定。

您不要质疑任何不违反Go Memory
Model的执行顺序。如果您希望执行达到代码中的某些点(执行某些语句),则需要显式同步(您需要同步goroutine)。

另请注意,Go
Playground上的输出已缓存,因此如果再次运行该应用程序,则不会再次运行该应用程序,而是会立即显示已缓存的输出。如果您更改了代码中的任何内容(例如,插入空格或注释),然后再次运行它,它将被编译并再次运行。您会注意到响应时间增加了。使用当前版本(Go
1.6
),您每次都会看到相同的输出。

在本地运行(在您的计算机上):

在本地运行它时,很有可能GOMAXPROCS会大于1默认值,因为它默认为可用的CPU核心数(从Go
1.5开始)。因此,是否有执行无穷循环的goroutine无关紧要,另一个goroutine将同时执行,即main(),当main()返回时,程序终止;它不会等待其他非main例程完成(请参见规范:程序执行)。

另请注意,即使您将设置GOMAXPROCS1,您的应用也很可能会在“很短的时间内”退出,因为调度程序的实现将切换到其他goroutine,而不仅仅是永久执行无限循环(但是,如上所述,这是不确定的)。然后,它将成为main()goroutine,因此当main()完成并返回时,您的应用程序终止。

在Go Playground上玩您的应用程序:

如前所述,默认情况下GOMAXPROCS位于1Go Playground上。但是,可以将其设置为更高的值,例如:

runtime.GOMAXPROCS(2)

没有显式同步,执行仍然是不确定的,但是您会观察到不同的执行顺序和终止,而不会发生超时:

Hello, playground
Here
Here
Here
...
<Here is printed 996 times, then:>
Finished.

Go Playground上尝试使用此变体。

2020-07-02