Golang中的goroutine处理bug

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

goroutine one process bug in golang

问题

我正在学习关于golang的知识。

在golang中,有一个概念叫做goroutine。我写了一个示例代码。

package main

import (
	"fmt"
	"runtime"
)

func main() {
	runtime.GOMAXPROCS(1) // 设置使用一个CPU核心

	s := "test string"

	for i := 0; i < 100; i++ {
		go func(n int) {
			fmt.Println(s, n)
		}(i)
	}

	fmt.Scanln()
}

如果你解释并运行这段代码,你会看到第一个打印的数据是:

test string 99

我不知道为什么会得到这个结果。有人可以帮助我吗?

英文:

I am studying about golang.

in golang, there is a concept, called goroutine. i write a sample code.

package main

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

func main() {
	runtime.GOMAXPROCS(1) // cpu core를 하나만 사용하도록 해 놓음

	s := &quot;test string&quot;

	for i := 0; i &lt; 100; i++ {
		go func(n int) {
			fmt.Println(s, n)
		}(i)
	}

	fmt.Scanln()
}

if you interpret code and run, you will see first printing data is

test string 99

I don't know why result it is. Anyone help me?

答案1

得分: 3

这是因为这一行代码:

runtime.GOMAXPROCS(1)

由于goroutine是一种并发执行代码的方式,你在循环中创建的所有goroutine都必须等待一个处理器。

由于你只有一个处理器,并且它正在忙于执行循环,你将会有一堆等待的goroutine。

当循环结束时,处理器有时间执行第一个goroutine,也就是最后创建的那个,然后依次对堆栈中的每个goroutine执行相同的操作,但这次是按顺序执行。

尝试将2个处理器放入并观察会发生什么。

请记住,在并发工作时,代码执行的顺序是不确定的,正如评论中已经提到的。

在这里,你有一个顺序,只是因为你只有一个进程。

如果你的目的是以并发的方式按顺序处理项目,你应该使用通道。

让我展示一个可以完成这个任务的代码示例:

package main

import (
    "fmt"
    "runtime"
)

func printNumbers(text string, ns chan int) {
    for n := range ns {
        fmt.Println(text, n)
    }
}

func main() {
    runtime.GOMAXPROCS(1) // cpu core를 하나만 사용하도록 해 놓음

    s := "test string"

    numbers := make(chan int)

    go printNumbers(s, numbers)

    go func() {
        for i := 0; i < 100; i++ {
            numbers <- i
        }
        close(numbers)
    }()

    fmt.Scanln()
}

正如你所看到的,这只是你的一个新版本,但如果你执行它,你应该得到正确的顺序。

主要的变化是现在有两个goroutine,一个用于打印,一个用于循环。

这两个goroutine通过一个int类型的通道进行通信。

当程序启动时,第一个goroutine调用函数printNumbers,它会等待直到有东西写入通道。

然后启动第二个goroutine,它逐个将整数写入通道。

一旦循环结束,由于只有一个进程,printNumbers函数会再次启动,因为通道中有项目。

它会循环直到处理完所有项目。

当然,这里有两个循环,但你无法避免它。

现在尝试根据你的意愿添加进程,看看会发生什么。

英文:

It is because of this line:

runtime.GOMAXPROCS(1)

As the goroutine are a way to execute code concurrently, all the goroutine you create in your loop have to wait for a proc.

As you have just one, and it is busy executing the loop, you're going to have a stack of goroutine waiting.

When the loops ends the proc have time to execute the first goroutine, that is the last created, and then do the same thing whith the stack one by one, but this time in sequence.

Try to put 2 procs and look what will happen.

Keep in mind that when you work in concurrent the order in which the code is executed is not guarantee, as already has been mentioned in a comment.

Here you have a sequence, just because you have just one process.

If your purpose is to have items processed sequencially even in a concurrent way, you should use channels.

Let me show a sample of code that do the job:

package main

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

func printNumbers(text string, ns chan int) {
    for n := range ns {
        fmt.Println(text, n)
    }
}

func main() {
    runtime.GOMAXPROCS(1) // cpu core를 하나만 사용하도록 해 놓음

    s := &quot;test string&quot;

    numbers := make(chan int)

    go printNumbers(s, numbers)

    go func() {
        for i := 0; i &lt; 100; i++ {
            numbers &lt;- i
        }
        close(numbers)
    }()

    fmt.Scanln()
}

As you can see is just a new version of your, but if you execute you should get the proper sequence.

The main changes are that you have 2 goroutine now, one for the print and one for the loop.

The 2 goroutine talk throungh a channel of int.

When the program start the first goroutin call the function printNumbers, that stai waiting until something is written into the channel.

Then start the second gorouting that write one by one the integers into the channel.

Once the for is ended, as you have just 1 process, the printNumbers function start again as there are items inside the channel.

It loops until it end the itmes.

Of course, you have 2 loops here, but you can't avoid it.

So now try to ass process as you wish and look what happen.

答案2

得分: 0

你的问题很好,答案基本上是关于内部运行时Go Scheduler的工作原理,但正如Mario Santini所说,执行顺序不能保证。

简单来说,Go Scheduler取决于内部使用的进程调度算法https://en.wikipedia.org/wiki/Scheduling_(computing),并基于此决定每个goroutine请求运行的时间和顺序,同时根据需要为您创建操作系统线程。在您的情况下,您将限制为仅有1个线程,因此根本不会并行执行,所以显然最后一个goroutine很幸运地作为第一个运行。

我给您留下一些更好解释的链接:

https://www.quora.com/How-does-the-golang-scheduler-work
http://www.sarathlakshman.com/2016/06/15/pitfall-of-golang-scheduler
http://www.slideshare.net/matthewrdale/demystifying-the-go-scheduler

英文:

Your question is a good one and the answer is basically how the internal runtime Go Scheduler works, but as Mario Santini says the order of execution isn't guarantee.

In my limited words I can tell you that the Go Scheduler depends of what kind of process scheduler algorithm https://en.wikipedia.org/wiki/Scheduling_(computing) is used internally and based on that it will decide how much time executed and the order of each goroutine asking to run, also it'll create OS threads for you as necessary, in your case you are limiting to only 1, so it'll not be parallel at all, so obviously the last goroutine is getting lucky enough to be run it as the first one.

I leave you some links with better explanations:

https://www.quora.com/How-does-the-golang-scheduler-work http://www.sarathlakshman.com/2016/06/15/pitfall-of-golang-scheduler http://www.slideshare.net/matthewrdale/demystifying-the-go-scheduler

huangapple
  • 本文由 发表于 2017年1月6日 14:55:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/41500528.html
匿名

发表评论

匿名网友

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

确定