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

可完成未来的完成处理程序在哪个线程中执行?

孔君浩
2023-03-14

我有一个关于CompletableFuture方法的问题:

public <U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> fn)

JavaDoc的意思是:

返回一个新的完成阶段,当此阶段正常完成时,将使用此阶段的结果作为所提供函数的参数来执行该阶段。有关异常完成的规则,请参阅完成阶段文档。

穿线呢?这将在哪个线程中执行?如果未来由线程池来完成呢?

共有3个答案

邴烨
2023-03-14

在Javadoc中:

为非异步方法的相关完成提供的操作可以由完成当前CompletableFuture的线程执行,或者由完成方法的任何其他调用方执行。

更具体地说:

>

  • fn将在调用完成()的线程的上下文中运行。

    如果完成()在调用Apply()时已经完成,则fn将在调用然后Apply()的线程上下文中运行。

  • 范弘亮
    2023-03-14

    “可完成的未来”文档中指定的策略可以帮助您更好地理解:

    > < li>

    为非异步方法的相关完成提供的操作可以由完成当前CompletableFuture的线程执行,或者由完成方法的任何其他调用方执行。

    所有没有显式执行器参数的异步方法都使用 ForkJoinPool.commonPool() 执行(除非它不支持至少两个的并行级别,在这种情况下,将创建一个新的线程来运行每个任务)。为了简化监视、调试和跟踪,所有生成的异步任务都是标记接口“可完成”的实例。

    更新:我还建议通过@Mike来阅读这个答案,作为对文档细节的进一步有趣分析。

    程旭尧
    2023-03-14

    正如@nullpoer所指出的,留档会告诉你你需要知道的。然而,相关文本出奇地模糊,这里发布的一些评论(和答案)似乎依赖于留档不支持的假设。因此,我认为值得把它拆开。具体来说,我们应该非常仔细地阅读这一段:

    为非异步方法的相关完成提供的操作可以由完成当前CompletableFuture的线程执行,或者由完成方法的任何其他调用方执行。

    听起来很简单,但细节很简单。它似乎有意避免描述何时可以在完成线程上调用依赖完成,而不是在调用完成方法(如然后Apply)期间调用依赖完成。如上所述,上述段落实际上要求我们用假设来填补空白。这是很危险的,尤其是当主题涉及并发和异步编程时,我们作为程序员的许多期望都被推翻了。让我们仔细看看文档中没有说什么。

    该留档不声明在调用完成()之前注册的依赖完成将在完成线程上运行。此外,虽然它声明在调用像thenApply这样的完成方法时可能会调用依赖完成,但它没有声明将在注册它的线程上调用完成(注意单词“任何其他”)。

    对于任何使用< code>CompletableFuture来计划和编写任务的人来说,这些都是潜在的要点。考虑这一系列事件:

    1. 线程A通过f.thenApply(c1)注册依赖完成
    2. 一段时间后,线程B调用f.complete()
    3. 大约同时,线程C通过f.thenApply(c2)注册另一个依赖完成

    从概念上讲,< code>complete()做两件事:它发布未来的结果,然后它尝试调用相关的完成。现在,如果线程C在结果值被提交之后,但在线程B开始调用< code>c1之前运行,会发生什么呢?根据实现的不同,线程C可能会看到< code>f已经完成,然后它可能会调用< code>c1和< code>c2。或者,线程C可以调用< code>c2,而让线程B调用< code>c1。文件没有排除任何一种可能性。记住这一点,以下是文档不支持的假设:

    1. 在完成之前在 f 上注册的从属完成 c 将在调用 f.complete()期间被调用;
    2. c 将在 f.complete() 返回时运行到完成;
    3. 依赖完成将以任何特定顺序(例如,注册顺序)调用;
    4. f 完成之前注册的依赖完成将在 f 完成后注册的完成之前调用。

    考虑另一个例子:

    1. 线程A调用f。complete()
    2. 一段时间后,线程B通过f注册完成。然后应用(c1)
    3. 大约在同一时间,线程C通过f.thenApply(c2)注册一个单独的完成

    如果已知f已经运行完毕,人们可能会认为c1将在 c2执行期间调用。可以进一步假设c1将在f之前运行完成。然后Apply(c1)返回。然而,文件并不支持这些假设。其中一个线程调用,然后Apply最终可能同时调用了c1c2

    仔细分析 JDK 代码可以确定上述假设场景可能如何发挥作用。但即使这样也是有风险的,因为您最终可能依赖于(1)不可移植或(2)可能发生变化的实现细节。你最好的选择是不要假设任何在javadocs或原始JSR规范中没有拼写出来的东西。

    tldr:小心你的假设,当你写文档时,要尽可能清晰和深思熟虑。虽然简洁是一件美妙的事情,但要警惕人类填补空白的倾向。

     类似资料:
    • 问题内容: 我对CompletableFuture方法有疑问: 事情是JavaDoc这么说的: 返回一个新的CompletionStage,当此阶段正常完成时,将使用该阶段的结果作为所提供函数的参数来执行该阶段。有关涵盖异常完成的规则​​,请参见CompletionStage文档。 那线程呢?这将在哪个线程中执行?如果将来由线程池完成怎么办? 问题答案: 文档中指定的策略可以帮助您更好地理解: 对

    • 我有一个关于CompletableFuture方法的问题: 问题是JavaDoc只说了这么一句话: 返回一个新的CompletionStage,当此阶段正常完成时,将以此阶段的结果作为所提供函数的参数执行该CompletionStage。有关例外完成的规则,请参阅CompletionStage文档。 穿线呢?这将在哪个线程中执行?如果未来是由一个线程池来完成的呢?

    • 我正在尝试在我的Swift应用程序中创建一个服务对象,以便更轻松地处理请求。我已经把它连接起来了,但是我可能误解了完成处理器。 我有这个函数,它只是发布到我正在运行的本地APIendpoint。 我需要做的就是把这个名字传给我在这里做的函数 但是,我现在收到此错误。 调用中缺少参数“completion”的参数 我的目标是返回< code>Response对象,这样我就可以访问它的属性,在UI中做

    • 我的完成处理程序有问题。下面是一个带有完成处理程序的函数,位于一个实用程序文件中: 我在视图控制器中调用它 输出清楚地表明该函数在运行该块之前没有等待完成: 我如何解决这个问题?

    • 我要做的是异步计算树结构的深度,我将有树的第一层,我想启动一个异步线程来分别计算每个节点的深度。 在计算过程中,树中显然可能有一个分叉,在这一点上,我想踢一个额外的线程来计算那个分支。 我已经得到了这个工作,但我需要做一些整理逻辑,当所有这些未来完成。但我对这一过程中产生的额外的可完成的未来感到困扰。 我会用什么方法来保存所有开始的CompletableFutures+那些动态创建的,并且在执行任

    • 我目前正在学习swift,几个小时以来我一直在试图理解一个错误。由于查询,我试图从php脚本中获取一个xml文件,我使用一个完成处理程序作为回调来获取这些查询的结果。 问题是我对这些技术感到不舒服,它说缺少论据。 下面是使用完成处理程序的函数: 这是我调用这个函数的一段代码: 这段代码显示有关调用连接的此错误: 我做错了什么?为什么它要两个参数? 非常感谢您的帮助。 埃尔巴托