【问题标题】:Why does scala hang evaluating a by-name parameter in a Future?为什么scala挂起评估未来中的按名参数?
【发布时间】:2013-11-23 02:58:27
【问题描述】:

下面的(人为的)代码尝试在未来打印一个按名称的字符串参数,并在打印完成时返回。

import scala.concurrent._
import concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

class PrintValueAndWait {
  def printIt(param: => String): Unit = {
    val printingComplete = future { 
      println(param);  // why does this hang?
    }
    Await.result(printingComplete, Duration.Inf)
  }
}

object Go {
  val str = "Rabbits"

  new PrintValueAndWait().printIt(str)
}

object RunMe extends App {
  Go
}

但是,在运行 RunMe 时,它只是在尝试评估 param 时挂起。将printIt 更改为按值接收其参数会使应用程序按预期返回。或者,将printIt 更改为简单地打印值并同步返回(在同一个线程中)似乎也可以正常工作。

这里到底发生了什么?这是否与尚未完全构建的 Go 对象有关,因此 str 字段对于尝试打印它的线程尚不可见?是否在此处挂起预期的行为?

我已经在 Mac OS Mavericks 和 Windows 7 以及 Java 1.7 上使用 Scala 2.10.3 进行了测试。

【问题讨论】:

    标签: multithreading scala object future pass-by-name


    【解决方案1】:

    您的代码在 Go 对象的初始化时出现死锁。这是一个已知问题,请参见例如SI-7646 和这个SO question

    scala 中的对象被延迟初始化,并在此期间锁定以防止两个线程竞相初始化对象。但是,如果两个线程同时尝试初始化一个对象,一个依赖另一个完成,就会出现循环依赖和死锁。

    在这种特殊情况下,Go 对象的初始化只能在 new PrintValueAndWait().printIt(str) 完成后完成。但是,当param 是按名称参数时,本质上是一个代码块被传入,在使用它时会对其进行评估。在这种情况下,new PrintValueAndWait().printIt(str) 中的str 参数是Go.str 的简写,因此当未来运行的线程尝试评估param 时,它实际上是在调用Go.str。但是由于Go还没有完成初始化,它也会尝试初始化Go对象。初始化Go 的另一个线程对其初始化有一个锁定,因此未来的线程阻塞。所以第一个线程在完成初始化之前等待future完成,future线程正在等待第一个线程完成初始化:死锁。

    在按值的情况下,直接传入str的字符串值,所以未来线程不会尝试初始化Go,不会出现死锁。

    同样,如果您将 param 保留为名称,但将 Go 更改如下:

    object Go {
      val str = "Rabbits"
    
      {
        val s = str
        new PrintValueAndWait().printIt(s)
      }
    }
    

    它不会死锁,因为传入的是已经评估的本地字符串值s,而不是Go.str,所以未来的线程不会尝试初始化Go

    【讨论】:

    • { val s = str new PrintValueAndWait().printIt(s) } 是做什么的?
    • 它与new PrintValueAndWait().printIt("Rabbits") 做同样的事情。通过使用匿名函数块,您可以声明局部变量s 以传递给printIt,并避免通过名称调用对Go 对象的依赖。
    • 但是我们是否需要 { 和 },如果我们省略它们并将“val s”设为私有会怎样?
    • 花括号定义了一个带有本地val s 的块,它不是Go 的成员。如果没有花括号,s 将是对象Go 的成员,而private 将在调用printIt 时导致死锁。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 2018-06-16
    相关资源
    最近更新 更多