gpt4 book ai didi

multithreading - 为什么 scala 在计算 Future 中的按名称参数时会挂起?

转载 作者:行者123 更新时间:2023-12-01 19:16:19 25 4
gpt4 key购买 nike

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

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 进行了测试。

最佳答案

您的代码在 Go 对象的初始化上陷入僵局。这是一个已知问题,请参见SI-7646还有这个SO question

scala 中的对象是延迟初始化的,并在此期间获取锁以防止两个线程争用初始化对象。但是,如果两个线程同时尝试初始化一个对象,并且一个线程依赖另一个线程来完成,则会出现循环依赖和死锁。

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

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

类似地,如果您将 param 保留为名称,但将 Go 更改如下:

object Go {
val str = "Rabbits"

{
val s = str
new PrintValueAndWait().printIt(s)
}
}

不会死锁,因为传入的是已计算的本地字符串值 s,而不是 Go.str,因此 future 的线程不会尝试初始化Go

关于multithreading - 为什么 scala 在计算 Future 中的按名称参数时会挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20157977/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com