1

org.apache.commons.math3.distribution.NormalDistribution在一个大型分布式 Scala & Akka 应用程序中使用。在调试过程sample()中,我发现偶尔会返回 NaN,它会默默传播并导致线程挂起org.apache.commons.math3.ode.nonstiff.DormandPrince853Integrator

NaN 可以通过并行集合简单地复制(不会在顺序代码中发生):

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)
}

显然,移动val normal内部foreach解决了这种情况下的问题。

我查看了文档,但看不到任何警告我此类问题的内容。我是否未能掌握有关线程安全的更基本概念?不用说我现在正在检查 NaN。

4

4 回答 4

3

通过挖掘源代码,您可以发现此构造函数使用Well19937c随机生成器,乍一看,它本身并不是线程安全的。

SynchronizedRandomGenerator您可以通过显式设置包含任何其他随机数生成器(如Well19937cor Mersenne Twister)的数字生成器来使其线程安全。请注意,通过同步对随机数生成器的访问,SynchronizedRandomGenerator您将失去所有潜在的性能优势,并且由于同步,“并行”版本可能会比顺序版本慢。另一方面,在每次迭代中并行重新初始化随机分布可能会根据当前时间使用相似的值多次重新播种 PRNG,因此您的结果会出现偏差。

一个非常普遍的经验法则(如果我在这里错了,请纠正我)是 99% 的时间,除非另有明确说明,否则在做任何依赖随机数生成的事情时,你可能应该坚持顺序执行,通常 PRNG 将存储从多个线程调用它们时可能会损坏的状态。除非您之后进行昂贵的计算,否则同步(在线程安全的有状态 PRNG 的情况下)将成为瓶颈。

于 2014-01-07T10:47:59.017 回答
2

一个中间立场是创建normal一个本地线程,也许是通过使用 Twitter 的Local实现

如果该normal.sample方法特别昂贵,这将有所帮助。您还可以确定不会同时在同一个线程上运行两个并行操作:)

于 2014-01-07T11:09:48.310 回答
1

尝试为每个线程使用另一个线程安全生成器或 NormalDistribution 实例,或同步访问单个实例。因为我认为您在多线程环境中使用了非线程安全对象

于 2014-01-07T10:55:19.690 回答
1

它的出现可能是因为您在多线程环境中使用了非线程安全对象(您同时调用方法示例两次或更多次)。您必须为每个线程使用另一个线程安全生成器或 NormalDistribution 实例或同步访问单个实例(可能会失去 par 执行的任何好处)。

于 2014-01-07T10:51:05.973 回答