2014-01-07 182 views
1

我在大型分佈式Scala & Akka應用程序中使用org.apache.commons.math3.distribution.NormalDistribution。在調試過程中,我發現sample()偶爾返回NaN的,這默默傳播,造成線程掛起org.apache.commons.math3.ode.nonstiff.DormandPrince853Integrator線程安全警告

楠可以簡單地用平行colelctions複製(串行代碼不會發生):

val normal = new NormalDistribution(0,0.1) 
(1 to 1000000000).par.foreach{i => 
    val r = normal.sample 
    if(r.isNaN()) throw new Exception("r = "+r) 
} 

顯然在foreach內移動val normal解決了這種情況下的問題。

我看過docs,但看不到任何警告我這類問題。我沒有把握關於線程安全的更基本概念嗎?不用說我現在正在檢查NaN。

回答

3

通過digging through sources您可以發現該構造函數使用Well19937c隨機生成器,該生成器乍一看本身並不是線程安全的。

您可以通過將數字發生器明確設置爲SynchronizedRandomGenerator(其中包含任何其他隨機數字生成器(如Well19937cMersenne Twister))來使其安全。請注意,通過將隨機數生成器的訪問與SynchronizedRandomGenerator同步,您將失去所有潛在的性能優勢,並且由於同步,「並行」版本可能會比順序版本慢。另一方面,並​​行重新初始化每次迭代的隨機分佈可能會基於當前時間使用相似的值多次重新生成PRNG,因此結果將會出現偏差。

拇指的一般規則(如果我在這裏錯了,請更正我)是99%的時間,除非另有明確規定,否則當做任何依賴的時候,你應該堅持順序執行隨機數生成,因爲通常PRNG將存儲從多線程調用它們時可能會損壞的狀態。除非事後做了昂貴的計算,否則同步(對於線程安全的有狀態PRNG)將是一個瓶頸。

+0

請注意,古老的'java.util.Random'既是線程安全的也是無鎖的... –

+0

@MichaelBorgwardt真的,很好!知道apache.commons.math的人可能會指導提出問題的人如何將其放入隨機分佈中,如果甚至可能的話。我真的不知道那個圖書館的事情,所以我無法幫助那裏... –

1

它的出現可能是因爲您在多線程環境中使用非線程安全對象(您正在調用方法採樣兩次或多次併發)。 您必須爲每個線程使用另一個線程安全的生成器或NormalDistribution的實例,或者同步對單個實例的訪問(可能會損失par執行的任何好處)。

1

嘗試爲每個線程使用另一個線程安全的生成器或NormalDistribution的實例,或者同步對單個實例的訪問。因爲我認爲你在多線程環境下使用非線程安全的對象

2

一箇中間地帶是通過使用Twitter的Local implementation來創建normal作爲本地線程,perhap。

如果normal.sample方法特別昂貴,這將有所幫助。你也可以肯定,沒有兩個並行操作將在同一個線程上同時運行:)