为什么scala Await.result在两次传递相同的未来时会在repl中超时?

时间:2014-04-16 11:55:52

标签: scala concurrency

我发现这有点令人困惑。我认为scala中的future是一次设置的不可变容器,总是返回相同的值。

所以我有一个未来:

val y = future {Thread.sleep(1000); 1};

现在,当我立即(在未来解决之前)将其传递给Await.result阻止两次:

Await.result(for (r1 <- y; r2 <- y) yield (r1, r2), 60 seconds)

我得到TimetoutException

但是如果我在未来解决后再这样做,一切正常并按预期返回(1,1)

这种行为的原因是什么?

编辑: 我正在使用隐式ExecutionContext.Implicits.globalscala.concurrent @ scala 2.10.3

EDIT2: 如果我创建另一个未来的实例做同样的事情,并对它们执行Await.result,它都不会阻塞。

2 个答案:

答案 0 :(得分:3)

这似乎是在REPL中执行它的工件。 您甚至可以使用2个单独的未来实例重现它,而无需调用Thread.sleep, 并且仅使用预先履行的期货(这意味着甚至没有任何未来的线程被调用)。 是的,严肃地说:

import scala.concurrent._
import duration._
import ExecutionContext.Implicits.global
val x = Future.successful(1)
val y = Future.successful(2)
Await.result(x.flatMap{_ => y.map{ _ => 0 } }, Duration(10, SECONDS)) // triggers a timeout

有趣的是,如果你将最后一行改为:

,这不会触发任何超时
Await.result(x.flatMap{_ => Future.successful(2).map{ _ => 0 } }, Duration(10, SECONDS))

似乎culprint是你的整个代码片段,当在REPL中被证实时,实际上是包装在一个对象中。 这意味着xy实际上是对象的成员,而不是局部变量
更重要的是,对Await的调用现在是此包装器对象的构造函数的一部分。 由于某些原因我尚未调查,似乎是Await的调用是在触发阻塞的构造函数中完成的 (您可以通过将此调用包装在虚拟类中并实例化它来轻松验证它。)

答案 1 :(得分:2)

只是补充堆栈跟踪,当闭包类需要X.z时失败:

apm@mara:~$ goof
Welcome to Scala version 2.11.0-RC3 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.concurrent._
import scala.concurrent._

scala> import duration._
import duration._

scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

scala> object X { val y = Future { 9 } ; val z = Future { 7 } ; val r = Await.result(for (a <- y; b <- z) yield (a+b), 5.seconds) }
defined object X

scala> X.r
java.lang.NoClassDefFoundError: Could not initialize class $line15.$read$$iw$$iw$$iw$$iw$$iw$$iw$X$
    at $line15.$read$$iw$$iw$$iw$$iw$$iw$$iw$X$$anonfun$3.apply(<console>:14)
    at $line15.$read$$iw$$iw$$iw$$iw$$iw$$iw$X$$anonfun$3.apply(<console>:14)
    at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:251)
    at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:249)
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
    at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
java.util.concurrent.TimeoutException: Futures timed out after [5 seconds]
  at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:219)
  at scala.concurrent.impl.Promise$DefaultPromise.result(Promise.scala:223)
  at scala.concurrent.Await$$anonfun$result$1.apply(package.scala:111)
  at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:53)
  at scala.concurrent.Await$.result(package.scala:111)
  ... 34 elided

只是为了证明它有效:

apm@mara:~$ goof -Yrepl-class-based
Welcome to Scala version 2.11.0-RC3 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.concurrent._
import scala.concurrent._

scala> import duration._
import duration._

scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

scala> object X { val y = Future { 9 } ; val z = Future { 7 } ; val r = Await.result(for (a <- y; b <- z) yield (a+b), 5.seconds) }
defined object X

scala> X.r
res0: Int = 16