【问题标题】:sync.WaitGroup and nested loopssync.WaitGroup 和嵌套循环
【发布时间】:2019-10-24 03:11:28
【问题描述】:

我想为迭代嵌套循环添加并发性,但遇到了麻烦。这个 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()

我明白了

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

所以你可能会看到它跳过了两个数组的第一个元素并只迭代最后一个元素。任何想法如何解决此行为?

【问题讨论】:

标签: go goroutine


【解决方案1】:

正如已经发布的那样,可以将值作为函数参数传递给 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()
        }()
    }
}

注意:上述修复只有在副本完成 goroutine 函数时才有效。


编辑:使用诸如 go vet 和 go 的 race-detector 之类的 go 工具来帮助捕获这些 gotcha 类型的错误。

例如,go playground(以及像 VScode 这样的流行编辑器)默认运行 go vet,例如

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

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

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

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

【讨论】:

  • 我不知道这个,有趣!我觉得它的可读性不是很明确。
  • @Clément 你的答案和这个答案都建议在Go FAQ 中。常见问题解答说这是更简单的方法。
  • @CeriseLimón 当然,这只是我个人的喜好。自从我学到了一些东西,我仍然投了赞成票。
  • 我也在答案中添加了go vet 和race-detector 信息。奇怪的是go vet 没有发现外循环问题,只有内循环问题。
【解决方案2】:

这是一个闭包问题。您需要在循环内将值传递给您的 goroutine,如下所示:

for _, originIata := range originCities {
    for _, destinationIata := range destinationCities {
        go func (originIata, destinationIata string) {
            fmt.Println(originIata)
            fmt.Println(destinationIata)
            wg.Done()
        }(originIata, destinationIata)
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2020-10-08
    • 1970-01-01
    • 2013-09-22
    • 2014-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多