Golang如何在goroutine之间共享变量?

use*_*268 6 concurrency multithreading go goroutine

我正在学习Go并试图了解其并发功能。

我有以下程序。

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)

        x := i

        go func() {
            defer wg.Done()
            fmt.Println(x)
        }()

    }

    wg.Wait()
    fmt.Println("Done")
}
Run Code Online (Sandbox Code Playgroud)

执行后,我得到:

4
0
1
3
2
Run Code Online (Sandbox Code Playgroud)

这就是我想要的。但是,如果我稍加修改:

4
0
1
3
2
Run Code Online (Sandbox Code Playgroud)

我得到的将是:

5
5
5
5
5
Run Code Online (Sandbox Code Playgroud)

我不太明白区别。谁能帮助解释这里发生的事情以及Go运行时如何执行此代码?

小智 10

每次运行都有新变量x := i
这段代码通过打印x内部 goroutine的地址很好地显示了差异:
Go Playground

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        x := i
        go func() {
            defer wg.Done()
            fmt.Println(&x)
        }()
    }
    wg.Wait()
    fmt.Println("Done")
}
Run Code Online (Sandbox Code Playgroud)

输出:

0xc0420301e0
0xc042030200
0xc0420301e8
0xc0420301f0
0xc0420301f8
Done
Run Code Online (Sandbox Code Playgroud)

并使用它构建您的第二个示例go build -race并运行它:
您将看到:WARNING: DATA RACE


这会很好The Go Playground

0xc0420301e0
0xc042030200
0xc0420301e8
0xc0420301f0
0xc0420301f8
Done
Run Code Online (Sandbox Code Playgroud)

输出:

0
4
1
2
3
Done
Run Code Online (Sandbox Code Playgroud)


cap*_*aig 7

一般规则是,不要在 goroutine 之间共享数据。在第一个示例中,您基本上为每个 goroutine 提供了自己的 副本x,然后它们以到达 print 语句的任何顺序将其打印出来。在第二个示例中,它们都引用了相同的循环变量,并且在它们中的任何一个打印它时都会增加到 5。我不相信那里的输出是有保证的,只是碰巧创建 goroutine 的循环完成得比 goroutine 本身到达打印部分的速度快。