F#并行序列中的奇异行为

时间:2011-10-02 14:33:46

标签: f# task-parallel-library

我编写了以下代码来生成某些数字的所有可能组合:

let allCombinations (counts:int[]) = 
   let currentPositions = Array.create (counts.Length) 0     
   let idx = ref (counts.Length-1) 
   seq{
       while currentPositions.[0]<counts.[0] do                        
           yield currentPositions           
           currentPositions.[!idx]<-currentPositions.[!idx]+1         
           while currentPositions.[!idx] >= counts.[!idx] && !idx>=1 do
               currentPositions.[!idx]<-0
               idx:=!idx-1
               currentPositions.[!idx]<-currentPositions.[!idx]+1               
           idx:=counts.Length-1            
   } 

我正在使用程序的其他部分中的序列,如下所示:

allCombinations counts |> Seq.map (fun idx -> buildGuess n digitsPerPos idx) ...

到目前为止一切顺利。程序按预期运行并生成组合。对于输入[| 2; 2; 2 |],它会生成八个值:

 [|0; 0; 0|]
 [|0; 0; 1|]
 [|0; 1; 0|]
 [|0; 1; 1|]
 [|1; 0; 0|]
 [|1; 0; 1|]
 [|1; 1; 0|]
 [|1; 1; 1|]

然而,当我使用PSeq并行生成的序列时,所有要消耗的值都变为[| 2; 0; 0 |],这是上面while循环中currentPositions数组的最后一个值。

如果我使用

yield (currentPositions|>Array.copy) 

而不是

yield currentPositions

顺序版和并行版都能正常工作。

为什么会这样;是否有最有效的方法来产生结果;提前谢谢你;

1 个答案:

答案 0 :(得分:3)

问题是你正在创建一个单个数组,你在迭代之间进行变异。

您可以通过构建结果列表而不是逐个打印出来来将并行性排除在等式之外 - 如果您先构建列表并然后将它们打印出来,那么您将看到相同的结果;该列表将包含相同的引用8次,始终包含在同一个数组实例中。

基本上为了避免副作用,你需要每个结果都独立于另一个 - 所以你应该每次创建一个单独的数组。