需要 Goroutine 示例解释

Goroutine example explaination needed

我刚刚开始学习 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()作为普通函数,打印"hello"然后退出。

那么哪个结果是正确的,为什么?

第一个输出将由单核机器生成。第二个可以由多核生成。

say 是一个内部有 for 循环的函数,迭代 5 次。它确实是一个普通函数,但其​​中有一个对 Gosched 的调用。 Gosched 所做的是,它告诉 运行time 暂停执行当前的 goroutine,而是启动另一个等待的 goroutine。这叫做yielding.

解释第一个输出

这是您期望在单核机器上获得的输出。一步一步来,

go say("world")

在这一步,运行时间开始在单独的 goroutine 上执行 say("world") 调用并继续主 goroutine。但是机器只有一个核心。所以两个 goroutines 不能 运行 parrallely。新的 goroutine(比如 gr A)必须等到 运行ning main goroutine(比如 gr B)完成或 pauses(yields)。所以它等待。主 goroutine 开始执行 say("hello")

现在在执行 gr Bsay 功能时 运行 时间遇到 runtime.Gosched()

Gosched 调用就像暂停。它告诉 运行time 暂停我并安排另一个正在等待的 goroutine。因此 运行 时间安排 gr A。它从等待的地方开始,也就是

say("world")

现在 gr A 执行直到遇到它自己的 runtime.Gosched()gr A 暂停。 gr B 醒来并从它离开的地方开始 运行ning。 runtime.Gosched()之后的语句是打印"hello"。所以 "hello" 被打印出来了。 gr B 继续并进入其 for 循环的下一次迭代。会见 Gosched。暂停。 gr A 重新启动。打印 "world"。我想您可以看到这是如何进行 5 次,直到它打印给定的输出。

解释第二个输出

如果你的机器有一个以上的核心,goroutines 可以运行并行。你的是你得到的输出。

现在调用 go say("world")gr A 不必等到 gr B 完成。它可以立即在另一个核心上启动。所以当 Gosched 被调用时,可能没有等待的 goroutines。如果当前的暂停,它将立即在另一个核心上启动。

所以在多核机器中你不能保证单词的打印顺序。如果你运行这个程序很多次我想你也会看到其他命令。

您可以将 GOMAXPROCs 设置为 1 并查看程序在单核机器上的表现如何 运行。

func main() {
    runtime.GOMAXPROCS(1)

    go say("world") // create a new goroutine
    say("hello")    // current goroutine
}

然后你会看到第一个输出。