我刚开始学习Go并按照包含goroutines上的以下示例的教程进行操作:
package main
import (
"fmt"
"runtime"
)
func say(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main() {
go say("world") // create a new goroutine
say("hello") // current goroutine
}
它声明“ runtime.Gosched() 意味着让CPU执行其他goroutine,并在某些时候返回。”。以下输出在示例下方给出:
hello
world
hello
world
hello
world
hello
world
hello
然而,当我在go run
的机器上运行此示例时,我得到了
hello
world
world
world
world
world
hello
hello
hello
hello
我的Go版本是go version go1.6 darwin/amd64
。
其实我不明白这两个结果!为什么不呢
hello
?据我所知,Go程序在程序的最后一个语句执行后退出,因此我认为在say()
作为goroutine运行并且其执行被延迟之后,程序将执行下一个say()
作为普通功能,打印“你好”,然后退出。
那么哪个结果是正确的?为什么?
答案 0 :(得分:3)
第一个输出是由单个核心机器生成的输出。第二个可以由多核的生成。
say
是一个内部有for循环的函数,迭代5次。它确实是一个普通的函数,但在其中调用了Gosched
。 Gosched
的作用是,它告诉运行时暂停执行当前的goroutine,而是启动另一个等待的goroutine。这称为让步。
解释第一个输出
这是您可以在单个核心计算机中获得的输出。一步一步走,
go say("world")
在此步骤中,运行时开始在单独的goroutine上执行say("world")
调用并继续主goroutine。但机器只有一个核心。因此两个goroutine都无法并行运行。新的goroutine(比如gr A
)必须等到正在运行的主goroutine(比如gr B
)完成或暂停(yeilds)。所以它等待。主goroutine开始执行say("hello")
现在,在执行say
运行时的gr B
函数时遇到runtime.Gosched()
Gosched
电话就像暂停。它告诉运行时暂停我并且正在等待另一个正在等待的goroutine。因此运行时调度gr A
。它从等待的地方开始,即
say("world")
现在gr A
会执行,直到遇到自己的runtime.Gosched()
。 gr A
暂停。 gr B
醒来并从它离开的地方开始跑步。 runtime.Gosched()
之后的陈述是打印&#34;你好&#34;。那么&#34;你好&#34;打印出来。 gr B
继续并进入其for循环的下一次迭代。符合Gosched
。暂停。 gr A
重启。打印&#34;世界&#34;。我想你可以看到它如何持续5次,直到打印出给定的输出。
解释第二个输出
如果您的机器有多个核心,则goroutine可以并行运行。你的是你得到的输出。
现在调用go say("world")
gr A
时,不必等到gr B
完成。它可以立即从另一个核心开始。因此,当调用Gosched
时,可能没有等待的goroutines。如果当前暂停,它将立即从另一个核心开始。
因此,在多核机器中,您无法保证单词的打印顺序。如果您多次运行该程序,我认为您也会看到其他订单。
您可以将GOMAXPROC设置为1,并查看程序如何在单个核心计算机上运行。
func main() {
runtime.GOMAXPROCS(1)
go say("world") // create a new goroutine
say("hello") // current goroutine
}
然后你会看到第一个输出。