为了解决我对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永远运行。
other
但是,当我在自己的机器上运行相同的代码时,几乎立即获得以下输出:
Hello, playground. Finished.
这似乎暗示other主goroutine完成时,其中的goroutine将退出。 这是真的? 还是主goroutine完成,而其他goroutine继续在后台运行?
在Go Playground上,GOMAXPROCS是1(证明)。
1
这意味着一次执行一个goroutine,并且如果该goroutine没有阻塞,则不会强制调度程序切换到其他goroutine。
您的代码(像每个Go应用程序一样)以执行main()函数的goroutine(主goroutine)开始。它启动另一个执行该other()功能的goroutine ,然后从done通道接收-阻塞。因此,调度程序必须切换到另一个goroutine(执行other()功能)。
main()
other()
done
在other()函数中,当您在done通道上发送值时,这将使当前(other())和maingoroutine可运行。调度选择继续运行other(),并且因为GOMAXPROCS=1,main()没有继续。现在other()启动另一个执行无限循环的goroutine。调度程序选择执行此goroutine,这将永远花费到阻塞状态,因此main()不会继续。
main
GOMAXPROCS=1
然后,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例程完成(请参见规范:程序执行)。
GOMAXPROCS
另请注意,即使您将设置GOMAXPROCS为1,您的应用也很可能会在“很短的时间内”退出,因为调度程序的实现将切换到其他goroutine,而不仅仅是永久执行无限循环(但是,如上所述,这是不确定的)。然后,它将成为main()goroutine,因此当main()完成并返回时,您的应用程序终止。
如前所述,默认情况下GOMAXPROCS位于1Go Playground上。但是,可以将其设置为更高的值,例如:
runtime.GOMAXPROCS(2)
没有显式同步,执行仍然是不确定的,但是您会观察到不同的执行顺序和终止,而不会发生超时:
Hello, playground Here Here Here ... <Here is printed 996 times, then:> Finished.
在Go Playground上尝试使用此变体。