问题
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))
}
回答1:
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:
- using
lazy val
- using
def
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)
to limit allocations of redundant objects, and that def
is better for a case like
def basePath = Base.LeftElement
since that's pretty likely to be inlined by JIT.
See also: How to diagnose or detect deadlocks in static initializers
来源:https://stackoverflow.com/questions/63149269/how-to-identify-get-automated-hints-with-cyclic-object-initialization-causing