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

Giu*_*ano 6 parallel-processing scala

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

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

50005000
Run Code Online (Sandbox Code Playgroud)

没问题.但如果尝试并行化,竞争条件就会发生:

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

49980037
Run Code Online (Sandbox Code Playgroud)

Dan*_*ral 17

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

更好的答案:

val sum = (1 to 10000).par.reduce(_+_) // depends on commutativity and associativity
Run Code Online (Sandbox Code Playgroud)

另见aggregate.


axe*_*l22 5

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

  1. sum入寄存器
  2. sum值添加到寄存器中
  3. 将更新后的值写回内存

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

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

所以,正如丹尼尔建议的那样 -reduce为此使用。