2012-03-30 100 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