我刚刚开始学习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执行其他goroutines,并在某个时候返回。“下面的示例给出了以下输出:
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(),然后打印"hello“,然后退出。
那么,哪个结果是正确的,为什么呢?
发布于 2016-03-28 12:40:57
第一个输出是由单个核心机器产生的输出。第二个可以由多核产生。
say是一个函数,里面有一个for循环,迭代5次。它确实是一个普通的函数,但是其中有一个对Gosched的调用。Gosched所做的就是告诉运行时暂停执行当前的goroutine,然后启动另一个等待的goroutine。这叫做屈服。
解释第一个输出
这是你可以期望在一台单核机器上得到的输出。一步一步,
go say("world")
在这个步骤中,运行库开始对单独的goroutine执行say("world")调用,并继续主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()后面的语句是打印"hello“。所以“你好”就印出来了。gr B继续并进入its for循环的下一个迭代。遇见了Gosched。停下来。gr A重新启动。打印“世界”。我想你可以看到这5次,直到它打印出给定的输出。
解释第二个输出
如果您的机器有多个核心,那么goroutines可以并行运行。你的输出是当你得到它的时候。
现在,当go say("world")被调用时,gr A不必等待gr B完成。它可以立即在另一个核心上启动。因此,当Gosched被调用时,就不会有等待的猩猩了。如果当前暂停,它将立即启动在另一个核心。
因此,在一台多核机器中,你不能保证单词的打印顺序。如果你多次运行这个程序,我想你也会看到其他订单的。
您可以将GOMAXPROCs设置为1,并查看该程序如何在一台核心计算机上运行。
func main() {
runtime.GOMAXPROCS(1)
go say("world") // create a new goroutine
say("hello") // current goroutine
}然后您将看到第一个输出。
https://stackoverflow.com/questions/36261502
复制相似问题