2012-03-30 76 views
6

并行集合是否打算做副作用操作?如果是这样,你如何避免竞争条件? 例如:如何避免使用scala并行集合的竞争条件

var sum=0 
(1 to 10000).foreach(n=>sum+=n); println(sum) 

50005000 

没有问题。 但是如果尝试并行化,竞态条件发生:

var sum=0 
(1 to 10000).par.foreach(n=>sum+=n);println(sum) 

49980037 
+0

不,副作用不好。如果你没有状态,那就很难有竞争条件。 – PlexQ 2012-03-31 01:32:27

回答

17

快速回答:不要这样做。并行代码应该是并行,不是并发的。

更好的答案:

val sum = (1 to 10000).par.reduce(_+_) // depends on commutativity and associativity 

aggregate见。

4

并行的情况下不工作,因为你不使用volatile变量,因此不能保证你写的知名度,因为你有多个线程执行以下操作:

  1. sum到寄存器
  2. 添加到寄存器与sum
  3. 更新值写回到存储器

如果2个线程做步骤1第一O然后继续以任何顺序完成上述其余步骤,它们将最终覆盖其中一个更新。

  1. 使用@volatile注释来确保sum在执行此类操作时的可见性。请参阅here
  2. 即使使用@volatile,由于增量的非原子性,您将会失去一些增量。您应该使用AtomicInteger s和它们的incrementAndGet
  3. 尽管使用原子计数器将确保正确性,但共享变量会极大地影响性能 - 您的共享变量现在成为性能瓶颈,因为每个线程都会尝试原子地写入相同的缓存行。如果你很少写这个变量,这不会是一个问题,但是由于你在每次迭代中都会这样做,因此在这里不会有加速 - 事实上,由于处理器之间的缓存行拥有权转移,它可能会变慢。

因此,正如丹尼尔所说 - 为此使用reduce