登录
首页 >  Golang >  Go问答

对于 Go 中令人尴尬的并行任务,惯用的解决方案是什么?

来源:stackoverflow

时间:2024-04-05 19:06:36 436浏览 收藏

积累知识,胜过积蓄金银!毕竟在Golang开发的过程中,会遇到各种各样的问题,往往都是一些细节知识点还没有掌握好而导致的,因此基础知识点的积累是很重要的。下面本文《对于 Go 中令人尴尬的并行任务,惯用的解决方案是什么?》,就带大家讲解一下知识点,若是你对本文感兴趣,或者是想搞懂其中某个知识点,就请你继续往下看吧~

问题内容

我目前正在关注以下代码的增强版本:

func embarrassing(data []string) []string {
  resultchan := make(chan string)
  var waitgroup sync.waitgroup
  for _, item := range data {
    waitgroup.add(1)
    go func(item string) {
      defer waitgroup.done()
      resultchan <- dowork(item)
    }(item)
  }

  go func() {
    waitgroup.wait()
    close(resultchan)
  }()

  var results []string
  for result := range resultchan {
    results = append(results, result)
  }
  return results
}

这简直让我大吃一惊。所有这一切都可以用其他语言表示为

results = parallelMap(data, doWork)

即使在 go 中不能这么容易地完成,是否还有比上面更好的方法?


解决方案


如果您需要所有结果,则不需要通道(以及关闭它的额外 goroutine)来传达结果,您可以直接写入结果切片:

func cleaner(data []string) []string {
    results := make([]string, len(data))

    wg := &sync.waitgroup{}
    wg.add(len(data))
    for i, item := range data {
        go func(i int, item string) {
            defer wg.done()
            results[i] = dowork(item)
        }(i, item)
    }
    wg.wait()

    return results
}

这是可能的,因为切片元素充当不同的变量,因此可以单独写入而无需同步。详情请参阅 Can I concurrently write different slice elements。您还可以免费获得与您输入的顺序相同的结果。

另一种变化:如果 dowork() 不会返回结果,但会获取应“放置”结果的地址,并且另外使用 sync.waitgroup 来发出完成信号,则可以“直接执行 dowork() 函数” “作为一个新的 goroutine。

我们可以为 dowork() 创建一个可重用的包装器:

func dowork2(item string, result *string, wg *sync.waitgroup) {
    defer wg.done()
    *result = dowork(item)
}

如果你有这种格式的处理逻辑,那么可以这样并发执行:

func cleanest(data []string) []string {
    results := make([]string, len(data))

    wg := &sync.waitgroup{}
    wg.add(len(data))
    for i, item := range data {
        go dowork2(item, &results[i], wg)
    }
    wg.wait()

    return results
}

另一种变化可能是将一个通道传递给 dowork(),它应该在该通道上传递结果。该解决方案甚至不需要 sync.waitgroup,因为我们知道要从通道接收多少元素:

func cleanest2(data []string) []string {
    ch := make(chan string)
    for _, item := range data {
        go doWork3(item, ch)
    }

    results := make([]string, len(data))
    for i := range results {
        results[i] = <-ch
    }
    return results
}

func doWork3(item string, res chan<- string) {
    res <- "done:" + item
}

最后一个解决方案的“弱点”是它可能会收集“无序”结果(这可能是问题,也可能不是问题)。可以通过让 dowork() 接收并返回项目的索引来改进此方法以保留顺序。详细信息和示例请参见How to collect values from N goroutines executed in a specific order?

以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于Golang的相关知识,也可关注golang学习网公众号。

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>