【问题标题】:Should I block on a Future - scala我应该阻止未来 - 斯卡拉
【发布时间】:2017-05-13 21:25:00
【问题描述】:

根据 Scala 文档,不应在 Future 上进行阻塞。

“如前所述,为了性能和防止死锁,强烈建议不要对 future 进行阻塞。future 上的回调和组合器是使用其结果的首选方式。但是,在某些情况下可能需要阻塞并且受 Futures 和 Promises API 的支持。”

在我的程序退出之前,如何确保我的所有 Futures 都已完成(并且它们的回调已完成)?我通常在 main 函数的末尾使用 Await.result 来确保所有 Futures 都已完成。

object ConcurrencyExample extends App {
val gpf= Future {some operations}
val ccf = Future{some operations}


val atbf = for {g <- gpf
c <- ccf if c == true}  yield {some operations}

//is it OK to use Await? If not, how do I ensure that all Futures have finished
        ?
Await.result(atbf,1000 millis )
 }

问题

  1. 使用 Await 是否错误?否则我的代码不会等待 Futures 完成
  2. 如果是这样,有什么替代方案?
  3. 如何确保 Future 及其回调在我的主程序退出之前完成?

【问题讨论】:

    标签: scala


    【解决方案1】:

    是的,您可以 Await.result 就您的情况。

    您可以使用Await.result 来保持主线程处于活动状态,以便未来完成

    小心 Await.result

    请注意,这适用于 Akka 和 play 应用

    Await.result 只有在绝对必要时才应谨慎使用。

    Await.result 阻塞正在运行的线程,直到给定的持续时间。阻塞线程将浪费宝贵的计算资源,因为该线程将无法进行任何有用的计算,例如处理新请求或算法中的数字运算等。

    所以,尽量避免使用Await.result

    但是,我们什么时候使用它(Await.result)?

    这是使用Await.result 的典型用例之一。

    假设您编写了一个包含主线程的程序,并且主线程内的所有计算都是异步的。现在,一旦您在主线程内开始异步计算。有人必须停止主线程的存在,直到异步计算完成,否则程序停止运行,您无法看到异步计算的结果。

    当应用程序开始运行时,有一个非守护线程,其工作是执行 main()。除非非守护线程完成,否则 JVM 不会自行退出。

    object Main {
     def main(args: Array[String]): Unit = {
      import scala.concurrent.Future
      import scala.concurrent.duration._
    
      val f = Future { //do something }
      //stop main thread till f completes
      Await.result(f, 10 seconds)
     }
    }
    

    Future 使用守护线程来运行。所以守护线程不能阻止JVM关闭。因此,即使非守护线程正在运行,JVM 也会关闭。

    在上述情况下,如果不是主线程退出并且计算停止,则没有其他方法可以期望停止(阻塞)主线程直到计算 f 完成。

    在大多数情况下,您不需要使用Await.result 和简单的Future 组合,使用mapflatMap 就足够了。

    使用 Await.result 的风险(一般都是阻塞代码)

    在基于事件的模型中线程用完

    在基于事件的模型中,如果您的阻塞代码需要很长时间才能返回,您将很快耗尽线程。在 playframework 中,任何阻塞调用都可能会降低应用程序的性能,并且应用程序会因为线程耗尽而变得非常缓慢。

    在非基于事件的模型中内存不足

    在每个请求模型的线程中。当您有阻塞呼叫需要很长时间才能退出/返回时。

    案例 1:如果您有固定的线程池,那么应用程序可能会用完线程。

    案例 2:如果您有动态增长的线程池,那么您的应用程序将遭受过多的上下文切换开销,并且还会因为内存中的阻塞线程过多而耗尽内存。

    在所有情况下,除了等待某些 IO 或其他事件之外,没有完成任何有用的工作。

    【讨论】:

    • 谢谢。在 Scala 中使用 Future 是否有更安全且性能风险更小的替代方法(如果我们使用 Await)?
    • @ManuChadha 不,在这种情况下你必须等待。您可以使用 await 来保持主线程处于活动状态以完成未来
    • @ManuChadha 另一种方法是使用 non-deamon 线程进行期货,但这是不好的做法,永远不应该使用。
    • 嗨@pamu,您提到“在大多数情况下,您不需要使用 Await.result 和使用 map 和 flatMap 的简单 Future 组合就足够了”。 map 或 flatmap on 不会阻塞主线程。所以在 map 或 flatMap 之后,你仍然需要运行 Await.result 对吗?。
    猜你喜欢
    • 2013-04-27
    • 2014-08-17
    • 2012-08-24
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    相关资源
    最近更新 更多