期货在计划终止前不会运行

时间:2012-05-12 16:29:34

标签: scala concurrency future

我试图在新的Scala 2.10 futures feature上重现这个例子。 我使用的代码是:

import scala.concurrent.Future
import scala.concurrent.future

object Test {
    def main(args: Array[String]) {
     println("Test print before future")
     val s = "Hello"
     val f = future {s + " future!"}
     f onSuccess {case v => println(v)}
     println("Test print after future")
    }
}

而不是打印:

Test print before future
Hello future!
Test print after future

它只是打印:

Test print before future
Test print after future

我知道为什么会有这种行为吗?我的scala编译器版本是2.10.0-20120507。

3 个答案:

答案 0 :(得分:30)

问题是你作为独立程序执行它,其主线程在其中一个工作线程可以执行“Hello future!”之前终止。 println。 (新期货库产生的线程是守护线程)。

您还可以使用Await对象(也在scala.concurrent中)等待,直到将来f完成:

import scala.concurrent._
import scala.concurrent.util._

object Test {
  def main(args: Array[String]) {
    println("Test print before future")

    val s = "Hello"
    val f = future {s + " future!"}
    f onSuccess {case v => println(v)}
    println("Test print after future")

    Await.ready(f, Duration.Inf)
  }
}

这可以打印:

Test print before future
Test print after future
Hello future!

或者,它可以打印“Hello future!”在“将来测试打印”之前,取决于线程安排。

同样,您可以强制主线程等到最后f之前完成println,如下所示:

import scala.concurrent._
import scala.concurrent.util._

object Test {
  def main(args: Array[String]) {
    println("Test print before future")

    val s = "Hello"
    val f = future {s + " future!"}
    f onSuccess {case v => println(v)}

    Await.ready(f, Duration.Inf)        

    println("Test print after future")
  }
}

哪个会打印:

Test print before future
Hello future!
Test print after future

但是,请注意,当您使用Await时,您正在屏蔽。这当然有必要确保您的主应用程序线程不会终止,但除非另有必要,否则通常不应使用。

(对于这样的情况,Await对象是一个必要的逃避舱口,但是在整个应用程序代码中使用它而不关心它的语义会导致执行速度变慢,并行性较低。如果你需要确保回调是例如,按照某些指定的顺序执行,还有其他选择,例如andThen上的mapFuture方法。)

答案 1 :(得分:1)

我认为这里的问题是时机。很可能你未来的代码是在单独的deamon线程中运行的。我认为应用程序完成得非常快,而且这个deamon线程没有足够的时间来正确执行(应用程序不会等待deamon线程完成)。但这也是非常依赖系统的行为。对我来说它打印:

Test print before future
Test print after future
Hello future!

然后退出(我正在使用Scala 2.10.0-M3)。您可以尝试按照以测试它 - 只需将主执行线程置于休眠状态几秒钟,然后查看是否打印Hello future!

import scala.concurrent.Future
import scala.concurrent.future

object Test {
    def main(args: Array[String]) {
        println("Test print before future")

        val s = "Hello"
        val f = future {s + " future!"}
        f onSuccess {case v => println(v)}

        println("Test print after future")

        Thread.sleep(3000) 
        println("Test print at the end.")
    }
}

答案 2 :(得分:1)

我只想补充一点,一般来说还有另一种可能性,即期货未运行:达到线程池限制。

在你的情况下,它可能只是一个时间问题,正如其他人所指出的那样,但是作为未来的参考,请考虑这个例子:

import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration


object FutureDebug {
  def main( args: Array[String] ) {

    for (i <- Range(0, 4)) {
      future {
        while (true) {
          Thread.sleep(1000)
          println("I'm doing stupid things in a future")
        }
      }
    }

    println("(1) reached? yes")
    val fut = future {
      for (i <- Range(0, 1000)) {
        println("never reached " + i)
      }
      3.14
    }    
    println("(2) reached? yes")
    Await.result(fut, Duration.Inf)
    println("(3) reached? no")
  }
}

在我的机器上,默认的全局执行上下文只有4个线程。由于工作线程正在忙于执行4个无意义的期货,因此下面的未来永远不会运行。这就是为什么人们应该谨慎使用默认的执行上下文,并且在处理多个(真正)长期运行的期货时最好specify one's own execution context