【问题标题】:Futures do not run before program termination期货在程序终止之前不运行
【发布时间】:2012-05-20 21:12:10
【问题描述】:

我试图在新的 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。

【问题讨论】:

    标签: scala concurrency future


    【解决方案1】:

    问题在于您将其作为独立程序执行,其主线程在其中一个工作线程可以执行“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 对象对于此类情况是必要的逃生舱口,但在整个应用程序代码中使用它而不考虑其语义可能会导致执行速度较慢、并行度降低。如果您需要确保在一些指定的顺序,例如,还有其他替代方案,例如Future 上的andThenmap 方法。)

    【讨论】:

    • 你知道吗?我想到了这个,因为当你处理 goroutines 时,这与 go 中的方式完全相同:在关闭主块之前,你使用通道等待 goroutines 发送的消息。
    • @Heather 是否有可能等待不止一个未来?
    • @AjOnFire 晚了 6 年,但是是的,Future.sequence() 将一系列期货变成了一个单一的未来,您可以在上面 await
    • @CervEd 我妈妈总是说迟到总比不到好。感谢您的更新!
    【解决方案2】:

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

    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.")
        }
    }
    

    【讨论】:

      【解决方案3】:

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

      在您的情况下,这可能只是其他人指出的时间问题,但作为未来参考,请考虑以下示例:

      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 个无意义的 future,因此下面的 future 将永远不会运行。这就是为什么人们应该小心默认执行上下文,并且在处理多个(真正)长期运行的期货时最好specify one's own execution context

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-31
        • 2020-11-03
        • 2015-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多