【问题标题】:Thread safety warnings线程安全警告
【发布时间】:2014-01-07 10:30:36
【问题描述】:

我在大型分布式 Scala 和 Akka 应用程序中使用 org.apache.commons.math3.distribution.NormalDistribution。在调试过程中,我发现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 中可以解决这种情况下的问题。

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

【问题讨论】:

    标签: java multithreading scala concurrency apache-commons-math


    【解决方案1】:

    通过digging through sources 你可以发现这个构造函数使用了Well19937c 随机生成器,乍一看它本身并不是线程安全的。

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

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

    【讨论】:

    • @MichaelBorgwardt 真的,很好!如果可能的话,了解 apache.commons.math 的人可能会指导提出问题的人如何将其插入随机分布。我真的对那个图书馆一无所知,所以我无能为力......
    【解决方案2】:

    中间立场是创建 normal 作为本地线程,也许是使用 Twitter 的 Local implementation

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-06-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-16
          • 1970-01-01
          • 2012-04-07
          相关资源
          最近更新 更多