如何使用scala并行集合来避免竞争条件

时间:2012-03-30 23:53:09

标签: scala parallel-processing

并行集合是否打算进行副作用操作?如果是这样,你怎么能避免竞争条件? 例如:

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

2 个答案:

答案 0 :(得分:17)

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

更好的回答:

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

另见aggregate

答案 1 :(得分:4)

并行情况不起作用,因为您不使用volatile变量,因此无法确保写入的可见性,并且因为您有多个线程执行以下操作:

  1. sum读入注册
  2. 使用sum
  3. 添加到寄存器
  4. 将更新后的值写回内存
  5. 如果2个线程先依次执行第1步,然后按任何顺序继续执行上述其余步骤,则最终会覆盖其中一个更新。

    1. 使用@volatile注释确保在执行此类操作时sum的可见性。见here
    2. 即使使用@volatile,由于增量的非原子性,您将失去一些增量。您应该使用AtomicInteger和他们的incrementAndGet
    3. 虽然使用原子计数器可以确保正确性,但是在这里使用共享变量会大大降低性能 - 您的共享变量现在是性能瓶颈,因为每个线程都会尝试以原子方式写入同一个缓存行。如果你不经常写这个变量,那就不会有问题了,但是既然你在每次迭代中都这样做,那么这里就没有加速 - 事实上,由于处理器之间的缓存行所有权转移,它可能会更慢
    4. 所以,正如丹尼尔建议的那样 - 使用reduce