gpt4 book ai didi

java - CompletableFuture#whenComplete 如果使用 thenApply 则不调用

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:16:22 24 4
gpt4 key购买 nike

我有以下代码(来自 my previous question )在远程服务器上安排任务,然后使用 ScheduledExecutorService#scheduleAtFixedRate 轮询是否完成。任务完成后,它会下载结果。我想将 Future 返回给调用者,以便他们可以决定何时阻塞以及阻塞多长时间,并为他们提供取消任务的选项。

我的问题是,如果客户端取消 download 方法返回的 Future,则 whenComplete block 不会执行。如果我删除 thenApply 它会。很明显我对 Future 组合有些误解...我应该更改什么?

public Future<Object> download(Something something) {
String jobId = schedule(something);
CompletableFuture<String> job = pollForCompletion(jobId);
return job.thenApply(this::downloadResult);
}

private CompletableFuture<String> pollForCompletion(String jobId) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
CompletableFuture<String> completionFuture = new CompletableFuture<>();

ScheduledFuture<?> checkFuture = executor.scheduleAtFixedRate(() -> {
if (pollRemoteServer(jobId).equals("COMPLETE")) {
completionFuture.complete(jobId);
}
}, 0, 10, TimeUnit.SECONDS);
completionFuture
.whenComplete((result, thrown) -> {
System.out.println("XXXXXXXXXXX"); //Never happens unless thenApply is removed
checkFuture.cancel(true);
executor.shutdown();
});
return completionFuture;
}

同样,如果我这样做:

return completionFuture.whenComplete(...)

代替

completionFuture.whenComplete(...);
return completionFuture;

whenComplete 也从不执行。这对我来说似乎非常违反直觉。从逻辑上讲,whenComplete 返回的 Future 不应该是我应该坚持的吗?

编辑:

我更改了我的代码以明确反向传播取消。它令人厌恶且难以阅读,但它确实有效,而且我找不到更好的方法:

public Future<Object> download(Something something) throws ChartDataGenException, Exception {
String jobId = schedule(something);
CompletableFuture<String> job = pollForCompletion(jobId);
CompletableFuture<Object> resulting = job.thenApply(this::download);
resulting.whenComplete((result, thrown) -> {
if (resulting.isCancelled()) { //the check is not necessary, but communicates the intent better
job.cancel(true);
}
});
return resulting;
}

编辑 2:

我发现了 tascalate-concurrent ,一个出色的库,提供了 CompletionStage 的合理实现,支持可以透明地向后传播取消的依赖 promise (通过 DependentPromise 类)。似乎非常适合这个用例。

这应该足够了:

DependentPromise
.from(pollForCompletion(jobId))
.thenApply(this::download, true); //true means the cancellation should back-propagate

请注意,没有测试这种方法。

最佳答案

你的结构如下:

           ┌──────────────────┐
│ completionFuture |
└──────────────────┘
↓ ↓
┌──────────────┐ ┌───────────┐
│ whenComplete | │ thenApply |
└──────────────┘ └───────────┘

因此,当您取消 thenApply future 时,原始 completionFuture 对象不受影响,因为它不依赖于 thenApply 阶段。但是,如果您不链接 thenApply 阶段,您将返回原始的 completionFuture 实例并取消此阶段会导致所有相关阶段的取消,从而导致 whenComplete 立即执行的操作。

但是当 thenApply 阶段被取消时,completionFuture 仍然可能在 pollRemoteServer(jobId).equals("COMPLETE") 条件已满足,因为轮询不会停止。但是我们不知道 jobId = schedule(something)pollRemoteServer(jobId) 之间的关系。如果您的应用程序状态发生变化,导致在取消下载后永远无法满足此条件,则此 future 将永远不会完成……


关于你的最后一个问题,哪个 future 是“我应该坚持的?”,没有要求有一个线性的 future 链,事实上,CompletableFuture 的便利方法使创建这样的链变得容易,但通常情况下,这是最没有用的事情,因为如果你有线性依赖,你可以只写一段代码。您链接两个独立阶段的模型是正确的,但取消不会通过它起作用,但它也不会通过线性链起作用。

如果您希望能够取消源阶段,您需要引用它,但如果您希望能够获得依赖阶段的结果,您也需要对该阶段的引用。

关于java - CompletableFuture#whenComplete 如果使用 thenApply 则不调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40258146/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com