sync.WaitGroup 和嵌套循环

Cro*_*ton 0 go goroutine

我想添加并发性来迭代嵌套循环,但遇到了麻烦。这个sync.WaitGroup 的示例用法有什么问题?

originCities := [3]string{"LED", "MOW", "PRS"}
destinationCities := [2]string{"UKT", "AAC"}

wg := &sync.WaitGroup{}
wg.Add(len(originCities) * len(destinationCities))

for _, originIata := range originCities {
    for _, destinationIata := range destinationCities {
        go func () {
            fmt.Println(originIata)
            fmt.Println(destinationIata)
            wg.Done()
        }()
    }
}
wg.Wait()
Run Code Online (Sandbox Code Playgroud)

我越来越

PRS AAC PRS AAC PRS AAC PRS AAC PRS AAC PRS AAC

正如您所看到的,它跳过两个数组的第一个元素并仅迭代最后一个元素。有什么想法可以解决此行为吗?

col*_*tor 5

正如已经发布的那样,可以将值作为函数参数传递给 goroutine 函数。

或者可以使用在循环范围内创建显式变量的技术。为简单起见,您可以重复使用相同的变量名称。这确保 goroutine 引用 for 循环的闭包值(而不是您遇到的外部范围的动态值):

for _, originIata := range originCities {
    originIata := originIata // here
    for _, destinationIata := range destinationCities {
        destinationIata := destinationIata // here
        go func () {
            fmt.Println(originIata)
            fmt.Println(destinationIata)
            wg.Done()
        }()
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:只有当复制是在goroutine 函数之外完成时,上述修复才有效。


编辑:使用 go 工具,如go vet和 go 的竞赛检测器来帮助捕获这些问题类型的错误。

例如,Go Playground(以及流行的编辑器,如 VScode)go vet默认运行,例如

https://play.golang.org/p/JhALssCu2-T

但请注意,不要依赖它go vet作为安全毯。在上面的操场上,它没有捕捉到外部o潜在的竞争条件。

您可以使用数据竞争检测器构建可执行文件(tl;dr;go build -race;将其用于测试而不是生产 - 因为它执行速度较慢并且具有类似于 8K go-routine 的限制)。

竞争检测器只会在运行时捕获数据竞争问题。因此请谨慎使用它,因为它不是代码流分析器,因此无法预测任何未来潜在的代码执行问题。