Scala:对象初始化器中的并行收集导致程序挂起
我刚刚注意到一个令人不安的行为.
假设我有一个单独的程序,由唯一的对象组成: object ParCollectionInInitializerTest { def doSomething { println("Doing something") } for (i <- (1 to 2).par) { println("Inside loop: " + i) doSomething } def main(args: Array[String]) { } } 该程序是完全无辜的,当在for循环中使用的范围不是并行的程序时,可以正确执行以下输出:
不幸的是,当使用并行集合时,程序只是挂起,而不用调用doSomething方法,所以输出如下:
然后程序挂起. 解决方法
这是在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挂起. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |