【问题标题】:Goroutine example explaination needed需要 Goroutine 示例说明
【发布时间】:2016-03-28 11:35:32
【问题描述】:

我刚刚开始学习 Go,并按照包含以下 goroutine 示例的教程进行操作:

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运行并延迟执行后,程序执行nextsay()作为普通函数,打印“你好”,然后退出。

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

【问题讨论】:

标签: go goroutine


【解决方案1】:

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

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

解释第一个输出

这是您可以期望在单核机器上获得的输出。一步一个脚印,

go say("world")

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

现在通过gr B运行时的say函数遇到runtime.Gosched()

Gosched 调用就像暂停。它告诉运行时暂停我并调度另一个正在等待的 goroutine。所以运行时调度gr A。它从它等待的地方开始,也就是,

say("world")

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

解释第二个输出

如果您的机器有多个内核,则 goroutine 可以并行运行。你的输出是你得到的输出。

现在当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
}

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

【讨论】:

  • 如果我有足够的内核并且执行流程满足Gosched() 可能实际上没有等待,我做对了吗?所以Gosched() 是一种说“如果你能继续运行,否则你可能会在这里切换到另一个 goroutine”?
  • 我认为它总是会暂停当前的 goroutine。并选择一个等待执行的 goroutine。如果唯一等待的 goroutine 是它刚刚暂停的那个,那么它将再次被选中。所以基本上你说的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
相关资源
最近更新 更多