加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala:对象初始化器中的并行收集导致程序挂起

发布时间:2020-12-16 19:02:26 所属栏目:安全 来源:网络整理
导读:我刚刚注意到一个令人不安的行为. 假设我有一个单独的程序,由唯一的对象组成: object ParCollectionInInitializerTest { def doSomething { println("Doing something") } for (i - (1 to 2).par) { println("Inside loop: " + i) doSomething } def main(a
我刚刚注意到一个令人不安的行为.
假设我有一个单独的程序,由唯一的对象组成:

object ParCollectionInInitializerTest {
  def doSomething { println("Doing something") }

  for (i <- (1 to 2).par) {
    println("Inside loop: " + i)
    doSomething
  }

  def main(args: Array[String]) {
  }
}

该程序是完全无辜的,当在for循环中使用的范围不是并行的程序时,可以正确执行以下输出:

Inside loop: 1
Doing something
Inside loop: 2
Doing something

不幸的是,当使用并行集合时,程序只是挂起,而不用调用doSomething方法,所以输出如下:

Inside loop: 2
Inside loop: 1

然后程序挂起.
这只是一个讨厌的错误吗?我正在使用scala-2.10.

解决方法

这是在Scala在施工完成之前发布对单例对象的引用时可能发生的固有问题.它发生在一个不同的线程尝试访问对象ParCollectionInInitializerTest之前,它已被完全构造.
它与main方法无关,而是与初始化包含main方法的对象有关 – 尝试在REPL中运行该对象,键入表达式ParCollectionInInitializerTest,并获得相同的结果.它也没有任何关系fork-join工作线程作为守护进程线程.

单例对象被初始化.每个单例对象只能初始化一次.这意味着访问对象的第一个线程(在你的情况下,主线程)必须抓住对象的锁,然后初始化它.随后的每个其他线程都必须等待主线程初始化对象并最终释放锁.这是在Scala中实现单例对象的方式.

在你的情况下,并行收集工作线程尝试访问单例对象来调用doSomething,但是在主线程完成初始化对象之前不能这样做,所以它等待.另一方面,主线程在构造函数中等待,直到并行操作完成,这是所有工作线程完成的条件 – 主线程始终保持单例的初始化锁定.因此,发生死锁.

您可能会导致此行为与期货从2.10,或只是线程,如下所示:

def execute(body: =>Unit) {
  val t = new Thread() {
    override def run() {
      body
    }
  }

  t.start()
  t.join()
}

object ParCollection {

  def doSomething() { println("Doing something") }

  execute {
    doSomething()
  }

}

将其粘贴到REPL中,然后写入:

scala> ParCollection

而REPL挂起.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读