当前位置: 首页 > 知识库问答 >
问题:

Scala的未来和执行上下文执行

韶亮
2023-03-14

假设我有以下一组代码,可以在将来做一些事情:

1 to 10 foreach {
  case x => Future { x + x }
}

假设我为这段代码提供了默认的ExecutionContext,我知道在后台会发生什么,但我想知道的是如何处理未来?我的意思是,应该有一些线程或一组线程可能会等待未来完成?这些线程被阻塞了吗?从某种意义上说,他们是在等待未来的结束?

现在在以下场景中:

val x: Future[MyType] = finishInSomeFuture()

假设x有一个超时,我可以这样调用:

Future {
  blocking {
    x.get(3, TimeOut.SECONDS)
  }
} 

我真的在阻挡吗?有没有更好的异步超时方法

编辑:下面的超时比我上面定义的阻塞上下文有什么不同或者更好?

object TimeoutFuture {
  def apply[A](timeout: FiniteDuration)(block: => A): Future[A] = {

    val prom = promise[A]

    // timeout logic
    Akka.system.scheduler.scheduleOnce(timeout) {
      prom tryFailure new java.util.concurrent.TimeoutException
    }

    // business logic
    Future { 
      prom success block
    }

    prom.future
  } 
}

共有1个答案

岳朝
2023-03-14

>

  • 假设我有以下一组代码,可以在将来执行某些操作:

    1 to 10 foreach {
      case x => Future { x + x }
    }
    

    ...

    您的代码段创建了十个 Future,这些 Futures 使用隐式 ExecutionContext 提供的线程立即设置为执行。由于您不存储对期货的引用,也不等待它们执行,因此您的主线程(定义foreach的位置)不会阻塞并立即继续执行。如果这段代码位于 main 方法的末尾,那么,根据 ThreadFactory in ExecutionContext 中是否生成守护程序线程,程序可能会退出而无需等待 Futures 完成。

    现在在以下场景中:

    val x: Future[MyType] = finishInSomeFuture()
    

    假设x有一个超时,我可以这样调用

    Future {
      blocking {
        x.get(3, TimeOut.SECONDS)
      }
    } 
    

    我真的在阻挡吗?有没有更好的异步超时方法?

    你的意思可能是 Await.result 而不是 x.get

    def inefficientTimeoutFuture[T](f:Future[T], x:Duration) = Future { Await.result(f, x) }
    

    在这种情况下,未来的f将在单独的线程中计算,而其他线程将被阻塞等待f的计算。

    使用调度程序创建TimeoutFuture效率更高,因为调度程序通常共享固定数量的线程(通常是一个),同时在<code>等待中阻塞。结果将始终需要额外的线程来阻止。

    我想知道如何在不阻塞的情况下超时?

    使用调度程序创建TimeoutFuture允许您在不阻塞的情况下超时操作。您正在将Future包装在timeout helper中,新的Future要么成功完成,要么由于超时而失败(无论先出现什么)。新的Future具有相同的异步性质,由您决定如何使用它(在完成回调中注册或同步等待结果,阻塞主线程)。

    UPD:我会试着澄清一些关于多线程和阻塞的基础知识。

    现在,异步非阻塞html" target="_blank">方法是趋势,但您必须了解阻塞的含义以及为什么应该避免阻塞。

    Java中的每个线程都是有代价的。首先,创建新线程的成本相对较高(这就是线程池存在的原因),其次,它会消耗内存。为什么不是CPU?因为您的CPU资源受到您拥有的内核数量的限制。无论您有多少个活动线程,您的并行度都将受到内核数量的限制。如果线程是不活动的(阻塞的),它不会消耗CPU。

    在当代Java应用程序中,您可以创建相当数量的线程(数千个)。问题是,在某些情况下,您无法预测需要多少线程。这就是异步方法发挥作用的时候。它说:与其阻塞当前线程,而其他一些线程完成其工作,不如将下一步包装回调并将当前线程返回到池中,以便它可以执行其他一些有用的工作。因此,几乎所有线程都忙于实际工作,而不仅仅是等待和消耗内存。

    现在以计时器为例。如果您使用基于netty的HashedWheelTimer,则可以由单个线程支持它并安排数千个事件。当您创建等待超时被阻止的未来时,您每个“计划”占用一个线程。因此,如果您安排了一千个超时,则最终会得到一千个阻塞的线程(这再次消耗内存,而不是cpu)。

    现在您的“主”未来(您想在超时中包装)也不必阻塞线程。例如,如果您在未来执行同步超文本传输协议请求,您的线程将被阻塞,但如果您使用基于netty的AsyncHttpClient(例如),您可以使用不占用线程的基于Promise-based的未来。在这种情况下,您可以拥有少量固定数量的线程来处理任意数量的请求(数十万)。

    UPD2

    但是应该有一些线程应该阻塞,即使在计时器的情况下,因为它必须等待超时millis。那么,好处是什么,在哪里呢?我仍然阻止,但可能是我在计时器情况下阻止较少,或者?

    这仅适用于一个特定场景:当您有主线程等待异步任务完成时。在这种情况下,您是对的,没有办法在不阻塞主线程的情况下超时包装操作。在这种情况下使用Timers没有任何意义。您只需要额外的线程来执行您的操作,而主线程则等待结果或超时。

    但通常Futures用于更复杂的场景,其中没有“主”线程。例如,想象异步Web服务器,请求进来,您创建Future来处理它并注册回调来回复。没有“主”线程来等待任何事情。

    或者另一个示例,您希望向外部服务发出 1000 个具有单个超时的请求,然后将所有结果收集到一个位置。如果您有该服务的异步客户端,则可以创建 1000 个请求,将它们包装在异步超时中,然后合并为一个 Future。您可以阻止主线程等待将来完成或注册回调以打印结果,但您不必创建1000个线程来等待每个单独的请求完成。

    因此,要点是:如果您已经有了同步流,并且您想在超时中包装它的某个部分,您唯一能做的就是阻塞您的当前线程,直到其他线程执行该工作。如果你想避免阻塞,你需要从一开始就使用异步方法。

  •  类似资料:
    • 有几种安装 Nest 应用程序的方法。您可以创建一个 Web 应用程序,微服务或只是一个 Nest 执行上下文 。 Nest 上下文是 Nest 容器的一个包装,它包含所有实例化的类。我们可以直接使用应用程序对象从任何导入的模块中获取现有实例。由于这一点,您可以充分利用 Nest 框架的优势,包括 CRON 任务,甚至可以在其上构建 CLI 。 为了创建一个 Nest 应用程序上下文,我们使用下面

    • 简介 从本章开始,我将陆续(翻译、转载、整理)http://dmitrysoshnikov.com/网站关于ECMAScript标标准理解的好文。 本章我们要讲解的是ECMAScript标准里的执行上下文和相关可执行代码的各种类型。 原始作者:Dmitry A. Soshnikov 原始发布: 2009-06-26 俄文原文:http://dmitrysoshnikov.com/ecmascrip

    • 我试图理解java中完整期货的非阻塞回调性质 有了上面的代码,我总是看到下面看到的输出 线程名称ForkJoinPool.common池工人-1 thenApply Thread name main thenApply Thread name main thenAcceptThread name main Thread name main 这个顺序似乎建议主线程等待所有Futures线程的执行。

    • 我使用的是SpringBatch 3.0。3并且需要一些关于不序列化作业执行上下文和步骤执行上下文的说明,因为我们有大型对象集,不希望将它们持久化到spring批处理表中。我们是否可以只存储短上下文而不是序列化对象?

    • 我试图用scala和Maven创建可执行jar。我正在使用maven-scala-plugin和maven-assembly-plugin,但在我看来,汇编插件被忽略了。我得到没有依赖项的jar和包含没有主类行的manifest。

    • 我有一个以下数据(alldata),它有SQL查询和视图名称。 我已经拆分并正确地将其分配给诱惑(alldata) 当我尝试执行查询并从中注册tempview或表时,它显示空指针错误。但是当我注释掉spark时,PRINTLN显示了表中的所有值。sql语句。 但是当我用spark.sql执行它时,它会显示以下错误,请帮助我出错的地方。 19/12/09 02:43:12错误执行器:在阶段4.0任务