问题描述
由于循环对象初始化,以下代码会在将来的超时中运行(在Scala 2.x和Dotty中,-Xcheckinit或-Ycheck-init在这里没有帮助)。在复杂的项目中,这些周期通常被很好地隐藏。是否有可能从编译器或至少在运行时寻求帮助?如何防止这种情况在多线程环境中发生?
The following code runs into future timeouts (in Scala 2.x and Dotty, -Xcheckinit or -Ycheck-init does not help here) because of cyclic object initialization. In complex projects these cycles usually are hidden very well. Is there any possiblity of getting help from the compiler or at least at runtime? How do you prevent this from happening in a multithreaded environment?
import scala.concurrent.Future
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
object Base {
val LeftElement = "Left"
val RightElement = "Right"
println("Base before: " + Thread.currentThread())
val all = Set(Left, Right)
println("Base after: " + Thread.currentThread())
}
object Left {
println("Left before: " + Thread.currentThread())
val basePath = Base.LeftElement
}
object Right {
println("Right before: " + Thread.currentThread())
val basePath = Base.RightElement
}
object Main extends App {
val f1 = Future(Left)
val f2 = Future(Right)
println(Await.result(f1, 1 second))
println(Await.result(f2, 1 second))
}
推荐答案
通常,编译器和JVM不能帮助您避免这种情况。
In general, the compiler and JVM will not help you avoid this.
解决此问题的最佳方法是延迟对周期的评估,例如:
The best you can do to address this is delay evaluation of the cycle by, for instance:
- 使用
lazy val
- 使用
def
- using
lazy val
- using
def
请注意,相对于简单的 val
会产生一些开销。我还没有做过实验,但是我怀疑 lazy val
(会花费一些同步费用)对于像
Note that either results in some overhead relative to a simple val
. I haven't done experiments, but I'd suspect that lazy val
(incurring the expense of some synchronization) is better for a case like
lazy val all = Set(Left, Right)
以限制分配的冗余对象,对于像这样的情况, def
更好
to limit allocations of redundant objects, and that def
is better for a case like
def basePath = Base.LeftElement
因为JIT很可能内联。
since that's pretty likely to be inlined by JIT.
另请参见:
这篇关于如何使用循环对象初始化识别/获取自动提示,从而在Scala中导致死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!