2014-01-07 158 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方法特别昂贵,这将有所帮助。你也可以肯定,没有两个并行操作将在同一个线程上同时运行:)