Goroutine示例解释需要

huangapple go评论71阅读模式
英文:

Goroutine example explaination needed

问题

我刚开始学习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") // 创建一个新的goroutine
    say("hello")    // 当前的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",然后退出。

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

英文:

I've just started to learn Go and follow a tutorial which contains the following example on goroutines:

package main

import (
    &quot;fmt&quot;
    &quot;runtime&quot;
)

func say(s string) {
    for i := 0; i &lt; 5; i++ {
        runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say(&quot;world&quot;) // create a new goroutine
    say(&quot;hello&quot;)    // current goroutine
}

it states that "runtime.Gosched() means let the CPU execute other goroutines, and come back at some point.". The following output is given below the example:

hello
world
hello
world
hello
world
hello
world
hello

However when I run this example on my machine with go run I get

hello
world
world
world
world
world
hello
hello
hello
hello

my version of Go is go version go1.6 darwin/amd64.

Actually I don't understand either result! Why isn't it just

hello

? As I understand Go programs quit after the last statement of the program is executed, so I'd think that after say() is run as a goroutine and its execution is delayed, the program executes next say() as an ordinary function, print "hello" and then exit.

So which result is the correct one and why?

答案1

得分: 3

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

say 是一个函数,内部有一个循环,循环执行5次。它确实是一个普通的函数,但是在其中调用了 GoschedGosched 的作用是告诉运行时暂停执行当前的 goroutine,并开始执行另一个等待的 goroutine。这被称为让出

解释第一个输出

这是你在单核机器上可以期望得到的输出。逐步解释:

go say("world")

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

现在,在执行 gr Bsay 函数时,运行时遇到了 runtime.Gosched()

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

say("world")

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

解释第二个输出

如果你的机器有多个核心,goroutine 可以并行运行。你在多核机器上运行时得到的就是这个输出。

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

因此,在多核机器上,你无法保证单词的打印顺序。如果你多次运行程序,可能会看到其他的顺序。

你可以将 GOMAXPROCs 设置为 1,看看程序在单核机器上的运行情况。

func main() {
    runtime.GOMAXPROCS(1)

    go say("world") // 创建一个新的 goroutine
    say("hello")    // 当前的 goroutine
}

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

英文:

First output is the one that will be generated by a single core machine. The second could be generated by a multicore one.

say is a function with a for loop inside, that iterates 5 times. It indeed is an ordinary function but there is a call to Gosched in it. What Gosched does is, it tell the runtime to pause executing the current goroutine and instead start another waiting goroutine. This is called yielding.

Explaining first output

This is the output you can expect to get in a single core machine. Going step by step,

go say(&quot;world&quot;)

At this step the runtime starts executing the say(&quot;world&quot;) call on a seperate goroutine and continue the main goroutine. But the machine only has a single core. So both goroutines cannot run parrallely. The new goroutine (say gr A) has to wait until the running main goroutine(say gr B) finishes or pauses(yeilds). So it waits. Main goroutine starts executing say(&quot;hello&quot;)

Now while going through the say function of gr B runtime meets runtime.Gosched()

The Gosched call is like pausing. It tells the runtime to pause me and shedule an other goroutine that is waiting. So the runtime schedules the gr A. It begins from where it was waiting, which is,

say(&quot;world&quot;)

Now gr A executes until it meets its own runtime.Gosched(). gr A pauses. gr B wakes up and start running from where it left. The statement after runtime.Gosched() is to print "hello". So "hello" is printed. gr B continues and enters the next iteration of its for loop. Meets Gosched. Pauses. gr A restarts. Prints "world". I think you can see how this goes on for 5 times until it prints the given output.

Explaining the second output

If your machine has more than one core, the goroutines can run in parallel. Yours is an output you get when it does.

Now when go say(&quot;world&quot;) is called gr A does not have to wait until gr B finishes. It can immediately start on an other core. So when Gosched is called there could be no waiting goroutines. If the current one pauses it will immediately start on a different core.

So in a multicore machine you can't guarantee the order the words are printed. If you run the program many times I think you will see other orders as well.

You can set GOMAXPROCs to 1 and see how the program would run on a single core machine.

func main() {
    runtime.GOMAXPROCS(1)

    go say(&quot;world&quot;) // create a new goroutine
    say(&quot;hello&quot;)    // current goroutine
}

Then you will see the first output.

huangapple
  • 本文由 发表于 2016年3月28日 19:35:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/36261502.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定