[英]Why does scala hang evaluating a by-name parameter in a Future?
下面(設計的)代碼嘗試在將來打印一個名字的String參數,並在打印完成時返回。
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
字段對於嘗試打印它的線程還不可見? 掛在這里的預期行為?
我已經在Java 1.7上使用Scala 2.10.3在Mac OS Mavericks和Windows 7上進行了測試。
您的代碼在Go
對象的初始化上發生死鎖。 這是一個已知問題,參見例如SI-7646和這個SO問題
scala中的對象被懶惰地初始化,並且在此期間進行鎖定以防止兩個線程競爭以初始化對象。 但是,如果兩個線程同時嘗試初始化一個對象而另一個線程依賴另一個完成,則會出現循環依賴關系和死鎖。
在這種特殊情況下, Go
對象的初始化只能在new PrintValueAndWait().printIt(str)
完成后完成。 但是,當param
是一個by name參數時,實際上傳遞了一個代碼塊,在使用它時會對其進行求值。 在這種情況下, str
在爭論new PrintValueAndWait().printIt(str)
是速記Go.str
,所以當未來上試運行的線程來評估param
它實質上調用Go.str
。 但由於Go
還沒有完成初始化,它也會嘗試初始化Go
對象。 初始化Go
的另一個線程對其初始化有一個鎖定,因此將來的線程會阻塞。 所以第一個線程在完成初始化之前等待將來完成,並且未來的線程正在等待第一個線程完成初始化:死鎖。
在by值的情況下, str
的字符串值直接傳入,因此將來的線程不會嘗試初始化Go
並且沒有死鎖。
同樣,如果您按名稱保留param
,但按如下方式更改Go
:
object Go {
val str = "Rabbits"
{
val s = str
new PrintValueAndWait().printIt(s)
}
}
它不會死鎖,因為傳入已經計算Go.str
本地字符串值s
而不是Go.str
,所以未來的線程不會嘗試初始化Go
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.