为了解决我对goroutines的一些误解,我去了Go操场并跑了this code:
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操场回来时出现错误:过程耗时太长。
这似乎暗示在other
内创建的goroutine会永远运行。
但是当我在自己的机器上运行相同的代码时,我几乎立即得到了这个输出:
Hello, playground.
Finished.
这似乎意味着当主要的goroutine结束时other
内的goroutine退出。 这是真的吗?或主要的goroutine完成,而另一个goroutine继续在后台运行?
答案 0 :(得分:1)
在Go Playground,GOMAXPROCS为1
(proof)。
这意味着一次执行一个goroutine,如果该goroutine没有阻塞,则不会强制调度程序切换到其他goroutine。
您的代码(与每个Go应用程序一样)以执行main()
函数(主要goroutine)的goroutine开头。它启动另一个执行other()
函数的goroutine,然后从done
通道接收 - 阻塞。因此调度程序必须切换到另一个goroutine(执行other()
函数)。
当您在other()
频道上发送值时,在done
功能中,这会使当前(other()
)和main
goroutine运行。调度程序选择继续运行other()
,由于GOMAXPROCS=1
,main()
不会继续运行。现在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
goroutines完成(参见Spec: Program execution)。
另请注意,即使您将GOMAXPROCS
设置为1
,您的应用很可能会在“短暂”时间内退出,因为调度程序的实现将切换到其他goroutines而不仅仅是执行无限循环永远(但是,如上所述,这是非确定性的)。当它发生时,它将是main()
goroutine,因此当main()
完成并返回时,您的应用终止。
如前所述,Go Goground默认GOMAXPROCS
为1
。但是,允许将其设置为更高的值,例如:
runtime.GOMAXPROCS(2)
如果没有显式同步,执行仍然是不确定的,但是你会观察到不同的执行顺序和终止,而不会遇到超时:
Hello, playground
Here
Here
Here
...
<Here is printed 996 times, then:>
Finished.
在Go Playground上尝试此变体。
答案 1 :(得分:0)
您将在屏幕上看到的内容是不确定的。或者更确切地说,如果您传递给频道的true
值被延迟,您会看到一些&#34;此处&#34;。
但通常Stdout是缓冲的,这意味着它不会立即打印,但数据会被累积,并且在达到最大缓冲区大小后会被打印出来。在你的情况下,&#34; here&#34;打印主要功能已经完成,因此过程结束。
经验法则是:主要功能必须是活着的,否则所有其他goroutine都会被杀死。