【问题标题】:How to Generate Combinations in Parallel in Golang如何在 Golang 中并行生成组合
【发布时间】:2020-03-31 01:27:27
【问题描述】:

我正在尝试使用 Golang 并行生成组合(例如,10 个数字中的每 6 个组合)。

我有一个串行运行的解决方案:Serial Code

对于项目数(n) = 3,样本量(r) = 2的情况,输出为:

Got  [1 2]
Got  [1 3]
Got  [2 3]

现在我已经尝试并行化,代码如下:Parallel Code。它不起作用,我不知道为什么。对于同样的问题,输出是:

Put  [3 3] into the channel.
Got  [3 3] out of the channel.
Put  [3 3] into the channel.
Got  [3 3] out of the channel.
Put  [3 3] into the channel.
Got  [3 3] out of the channel.

非常感谢任何帮助。

【问题讨论】:

    标签: go parallel-processing goroutine


    【解决方案1】:

    首先,存在数据竞争。

    $ go run -race main.go
    ==================
    WARNING: DATA RACE
    Write at 0x00c0000b0018 by goroutine 11:
      main.combinationUtil()
          /home/leaf/spike/stackoverflow/concomb/main.go:33 +0x11e
    
    Previous write at 0x00c0000b0018 by goroutine 9:
      main.combinationUtil()
          /home/leaf/spike/stackoverflow/concomb/main.go:33 +0x11e
    
    Goroutine 11 (running) created at:
      main.combinationUtil()
          /home/leaf/spike/stackoverflow/concomb/main.go:35 +0x211
    
    Goroutine 9 (running) created at:
      main.combinationUtil()
          /home/leaf/spike/stackoverflow/concomb/main.go:35 +0x211
    ==================
    ==================
    WARNING: DATA RACE
    Read at 0x00c0000b0010 by goroutine 12:
      runtime.slicecopy()
          /usr/lib/go-1.13/src/runtime/slice.go:197 +0x0
      main.combinationUtil()
          /home/leaf/spike/stackoverflow/concomb/main.go:21 +0x39c
    
    Previous write at 0x00c0000b0010 by goroutine 10:
      main.combinationUtil()
          /home/leaf/spike/stackoverflow/concomb/main.go:33 +0x11e
    
    Goroutine 12 (running) created at:
      main.combinationUtil()
          /home/leaf/spike/stackoverflow/concomb/main.go:35 +0x211
    
    Goroutine 10 (running) created at:
      main.combinationUtil()
          /home/leaf/spike/stackoverflow/concomb/main.go:40 +0x2dc
    ==================
    

    这种数据竞争是由同时写入data 的底层数组引起的——这意味着data 的底层数组(因此data 的内容)在所有goroutine 上共享。这是不希望的。

    在 Go 中,slice 的底层是一个切片头(参见reflect.SliceHeader),它将lencapptr 保存在三个字节中到底层数组。当您复制它时,它不会复制底层数组,而只是复制标头,因此底层数组是共享和竞争的 - 两者都不需要。

    为避免这种情况,只需制作一个新副本即可。 (使用同步技术可以避免种族,但内容仍然是共享的)。然而,就时间和空间而言,仅此一项就是一项成本操作。这是否值得拥有并行的好处(如果有任何好处),是另一个话题,超出了这个答案的范围。

    例如,

    newdata := make([]int, r)
    copy(newdata, data)
    
    // current is included, put next at next location
    newdata[index] = arr[i]
    wg.Add(1)
    go combinationUtil(ch, wg, arr, n, r, index+1, newdata, i+1)
    
    // current is excluded, replace it with next (Note that
    // i+1 is passed, but index is not changed)
    wg.Add(1)
    go combinationUtil(ch, wg, arr, n, r, index, newdata, i+1)
    

    游乐场:https://play.golang.org/p/YebyCGapSMs

    注意 1:这种简单的添加副本仅因为递归不依赖于 data (data[index:]) 的部分更改而起作用。否则,需要有newdatadata 的备份。

    注 2:正如我之前暗示的,我怀疑这种并行的效率如何。可以有其他的并行计算组合的方法,可以更好地利用并行计算的能力,但是需要完全不同的算法。不过,我不确定,所以如果有兴趣,你应该自己研究一下。

    【讨论】:

    • 太棒了,非常感谢。是的,我听说您使用不同的算法和复制的时间/空间方面。
    猜你喜欢
    • 2021-07-13
    • 2018-03-19
    • 1970-01-01
    • 2016-07-04
    • 2021-06-12
    • 2021-01-06
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    相关资源
    最近更新 更多