为什么在一个包含两个 goroutine 的循环中,最后一个 goroutine 先启动?

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

Why the last goroutine starts first in a loop with two goroutines?

问题

我已经对goroutines进行了一些测试,并注意到了在使用这段代码时出现了一种奇怪的行为(对我来说)。以下是代码:

package main

import (
	"fmt"
	"sync"
)

var (
	mu sync.Mutex
	wg sync.WaitGroup
)

func main() {
	var x int
	for i := 0; i < 10; i++ {
		wg.Add(2)
		go func() {
			mu.Lock()
			x = i
			mu.Unlock()
			wg.Done()
		}()
		go func() {
			mu.Lock()
			fmt.Print("x: ", x, " \n")
			mu.Unlock()
			wg.Done()
		}()
		wg.Wait()
	}
}

我期望的输出应该是:

x: 0 
x: 1 
x: 2 
x: 3 
x: 4 
x: 5 
x: 6 
x: 7 
x: 8 
x: 9 

但我收到的输出是:

x: 0 
x: 0 
x: 1 
x: 2 
x: 3 
x: 4 
x: 5 
x: 6 
x: 7 
x: 8 

看起来第二个goroutine先被调用(类似LIFO)。考虑到这一点,我尝试颠倒goroutine的顺序,我得到了我期望的答案:

package main

import (
	"fmt"
	"sync"
)

var (
	mu sync.Mutex
	wg sync.WaitGroup
)

func main() {
	var x int
	for i := 0; i < 10; i++ {
		wg.Add(2)
		go func() {
			mu.Lock()
			fmt.Print("x: ", x, " \n")
			mu.Unlock()
			wg.Done()
		}()
		go func() {
			mu.Lock()
			x = i
			mu.Unlock()
			wg.Done()
		}()
		wg.Wait()
	}
}

输出结果为:

x: 0 
x: 1 
x: 2 
x: 3 
x: 4 
x: 5 
x: 6 
x: 7 
x: 8 
x: 9 

有人可以帮助我理解这种行为吗?

Go版本:
go version go1.16.2 linux/amd64

英文:

I have been doing some tests with goroutines and I noticed a weird behaviour (for me) using this code:

Playground: https://play.golang.org/p/Py4oqGqkYKm

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)
var (
	mu sync.Mutex
	wg sync.WaitGroup
)

func main() {
	var x int
	for i := 0; i &lt; 10; i++ {
		wg.Add(2)
		go func() {
			mu.Lock()
			x = i
			mu.Unlock()
			wg.Done()
		}()
		go func() {
			mu.Lock()
			fmt.Print(&quot;x: &quot;, x, &quot; \n&quot;)
			mu.Unlock()
			wg.Done()
		}()
		wg.Wait()
	}
}

I have expected as output something such as:

x: 0 
x: 1 
x: 2 
x: 3 
x: 4 
x: 5 
x: 6 
x: 7 
x: 8 
x: 9 

But I have received:

x: 0 
x: 0 
x: 1 
x: 2 
x: 3 
x: 4 
x: 5 
x: 6 
x: 7 
x: 8 

It looks like that the second goroutines is called first (like LIFO). Thinking that, I have tried to invert the goroutines and I received the answer that I have expected:

Playground: https://play.golang.org/p/BC1r3NK6RBm

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

var (
	mu sync.Mutex
	wg sync.WaitGroup
)

func main() {
	var x int
	for i := 0; i &lt; 10; i++ {
		wg.Add(2)
		go func() {
			mu.Lock()
			fmt.Print(&quot;x: &quot;, x, &quot; \n&quot;)
			mu.Unlock()
			wg.Done()
		}()
		go func() {
			mu.Lock()
			x = i
			mu.Unlock()
			wg.Done()
		}()
		wg.Wait()
	}
}

Output:

x: 0 
x: 1 
x: 2 
x: 3 
x: 4 
x: 5 
x: 6 
x: 7 
x: 8 
x: 9 

Could anyone help me to understand this behavior?

Go version:
go version go1.16.2 linux/amd64

答案1

得分: 4

Go语言不指定goroutine在没有使用通道、互斥锁、等待组等显式同步的情况下执行的顺序。规范允许两个程序的输出都有可能。

你观察到的是互斥锁的获取顺序,而不是goroutine启动的顺序。可能是goroutine按照你期望的顺序启动,但是意外的goroutine先调用了Lock()。

程序仅通过等待组确保了顺序:每对goroutine在下一对启动之前都会完成。

英文:

The Go Language does not specify the order that goroutines execute outside of explicit synchronization using channels, mutexes, wait groups and so on. The specification allows for both outputs from both programs.

You are observing the order that the mutex is acquired, not the order that the goroutines start. It could be that the goroutines start in your expected order, but the unexpected goroutine calls Lock() first.

The only ordering ensured by the program is through the wait group: Each pair of goroutines will complete before the next pair is started.

答案2

得分: 0

Go协程提供并发性,并在独立的堆分配和栈内存上工作。它们的目的是在多核系统上提供并发执行。它们的并行执行方式没有统一的规定,而是由实际的处理器决定。我们只通过抽象层次来使用处理器。

实际上,没有一个并发系统可以控制/预测其结果。

英文:

The Go routines provide concurrency and work on top of share heap allocation with independent stack memory. The purpose of them is to provide concurrent execution on top of multi-core systems. There in no governing factor to how they are executed in parallel. This is decided by the actual processor. We only use the processor through layers of abstraction.

There is actually no concurrent system where you can govern/predict the outcome.

huangapple
  • 本文由 发表于 2021年7月3日 13:40:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/68233545.html
匿名

发表评论

匿名网友

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

确定