为什么最后一个 goroutine 在有两个 goroutine 的循环中首先开始?

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

我一直在用 goroutines 做一些测试,我注意到使用这段代码有一个奇怪的行为(对我来说):

游乐场:https://play.golang.org/p/Py4oqGqkYKm

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)。考虑到这一点,我尝试反转 goroutines 并且收到了我预期的答案:

游乐场:https://play.golang.org/p/BC1r3NK6RBm

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 version go1.16.2 linux/amd64

Go 例程提供并发性,并在具有独立堆栈内存的共享堆分配之上工作。它们的目的是在多核系统之上提供并发执行。它们如何并行执行没有任何控制因素。这是由实际处理器决定的。我们仅通过抽象层使用处理器。

实际上没有可以govern/predict结果的并发系统。

Go 语言没有指定 goroutines 在显式同步之外使用通道、互斥锁、等待组等执行的顺序。该规范允许两个程序的两个输出。

您正在观察获取互斥量的顺序,而不是 goroutine 启动的顺序。可能是 goroutines 以您预期的顺序启动,但意外的 goroutine 先调用了 Lock()。

程序确保的唯一顺序是通过等待组:每一对 goroutine 将在下一对 goroutine 开始之前完成。