我正在学习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")
}

当我被处决时:

4
0
1
3
2

这正是我想要的.但是,如果我稍微修改一下:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

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

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

    }

    wg.Wait()
    fmt.Println("Done")
}

我得到的将是:

5
5
5
5
5

我不太明白有什么不同.有人能解释一下这里发生了什么吗?Go Runtime是如何执行这段代码的?

推荐答案

You have new variable on each run of x := i,
This code shows difference well, by printing the address of x inside goroutine:
The 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")
}

输出:

0xc0420301e0
0xc042030200
0xc0420301e8
0xc0420301f0
0xc0420301f8
Done

并使用go build -race构建第二个示例并运行:
你会看到:WARNING: DATA RACE


这将是罚款The Go Playground:

//go build -race
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            fmt.Println(i)
        }(i)
    }
    wg.Wait()
    fmt.Println("Done")
}

输出:

0
4
1
2
3
Done

Go相关问答推荐

将Go程序导出到WASM—构建约束排除所有Go文件

如何使用Docker Compose配置Go,使main. go文件位于/CMD文件夹中

在Golang中Mergesort的递归/并行实现中出现死锁

Docker Compose Health Check未退出,错误为无法启动

GORM:一个表的两个外键

Prometheus 摘要分位数错误

调用库和直接操作效率有区别吗?

无效操作:v > max(类型参数 T 与 > 不可比较)

无法在go中为docker容器写入有效的挂载路径

将字符串格式的x509证书生成主题名称

Opensearch 错误 ping 弹性服务器:由未知权威签署的 x509 证书

使用 Go Colly 抓取所有可能的标签并将它们放入一个变量中

有没有办法计算枚举中定义的项目总数?

Gorm delete with clauses sqlmock 测试

regex.ReplaceAll 但如果替换则添加相同数量的字符

AddE 上的 Apache Tinkerpop gremlin-go 驱动程序 Next() 返回E0903:没有剩余结果

如何模仿联合类型

如何使用特定的 Go 版本运行 govulncheck?

在 Go 中将十六进制转换为带符号的 Int

Golang 泛型同时具有接口和实现