Go中令人难以置信的并行任务的惯用解决方案是什么?

Seb*_*off -2 concurrency channel go goroutine

我目前正在盯着以下代码的增强版本:

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
}
Run Code Online (Sandbox Code Playgroud)

这只是在想我的想法.所有这一切都可以用其他语言表达为

results = parallelMap(data, doWork)
Run Code Online (Sandbox Code Playgroud)

即使在Go中不能轻易做到这一点,是不是还有比上面更好的方法呢?

icz*_*cza 8

如果您需要所有结果,则不需要通道(以及关闭它的额外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
}
Run Code Online (Sandbox Code Playgroud)

这是可能的,因为切片元素充当不同的变量,因此可以单独写入而无需同步.有关详细信息,请参阅可以同时写入不同的切片元素.您也可以按照与输入相同的顺序获得结果.

Anoter变化:如果doWork()不返回结果但得到结果应该"放置"的地址,另外还有sync.WaitGroup信号完成,该doWork()功能可以"直接"作为新的goroutine执行.

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

func doWork2(item string, result *string, wg *sync.WaitGroup) {
    defer wg.Done()
    *result = doWork(item)
}
Run Code Online (Sandbox Code Playgroud)

如果您具有这种格式的处理逻辑,那么它是如何同时执行的:

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
}
Run Code Online (Sandbox Code Playgroud)

另一种变化可以是将信道传递到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
}
Run Code Online (Sandbox Code Playgroud)

最后一个解决方案的"弱点"是它可能收集"无序"结果(这可能是也可能不是问题).通过允许doWork()接收和返回项目的索引,可以改进此方法以保留顺序.有关详细信息和示例,请参阅如何从按特定顺序执行的N个goroutines中收集值?