需要Goroutine示例解释

时间:2016-03-28 11:35:33

标签: go goroutine

我刚开始学习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()作为普通功能,打印“你好”,然后退出。

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

1 个答案:

答案 0 :(得分:3)

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

say是一个内部有for循环的函数,迭代5次。它确实是一个普通的函数,但在其中调用了GoschedGosched的作用是,它告诉运行时暂停执行当前的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
}

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