[英]Recursively cancel an allOf CompletableFuture
If I have 如果我有
CompletableFuture<Something> future1 = service.request(param1);
CompletableFuture<Something> future2 = service.request(param2);
CompletableFuture<Void> many = CompletableFuture.allOf(future1, future2);
what will happen when I do many.cancel()
? 当我做
many.cancel()
时会发生什么? Will future1
and future2
be cancelled as well? future1
和future2
被取消吗? If not, what would be the cleanest way to achieve this? 如果没有,最简单的方法是什么? I'm reluctant to hold on to
future1
and future2
, just to be able to cancel them when I want to cancel many
. 我不愿意坚持
future1
和future2
,只是为了能够在取消many
时取消它们。
Some background on why I want this: when receiving a piece of data, I need to request matching, potentially future data to perform a computation. 关于为什么我想要这个的一些背景:当接收一条数据时,我需要请求匹配的,可能未来的数据来执行计算。 If a newer piece of data arrives, I want to cancel the completion of the earlier computation, because the result will immediately be superceded by the new computation.
如果有更新的数据到达,我想取消先前计算的完成,因为结果将立即被新计算取代。
Before you make you life harder than necessary, you should become aware of what cancelling a CompletableFuture
actually does. 在你让生活变得艰难之前,你应该知道取消
CompletableFuture
实际上做了什么。 Most important, it does not stop the associated computation. 最重要的是,它不会停止相关的计算。
If a computation associated with a CompletableFuture
is already running, but has not completed yet, cancelling a CompletableFuture
turns it into the “cancelled” state, which may have an immediate effect on all dependent stages, but not on the computation, which will continue until complete, though its attempt to complete the cancelled future will not have any effect. 如果与
CompletableFuture
相关联的计算已经运行但尚未完成,则取消CompletableFuture
会将其转换为“已取消”状态,这可能对所有相关阶段产生直接影响,但不会影响计算,这将持续到完成,虽然它试图完成取消的未来将不会有任何影响。
While other Future
's might be cancelled with interruption, which will stop the computation, if it checks for interruption, this doesn't apply to CompletableFuture
, see CompletableFuture.cancel(boolean)
: 虽然其他
Future
可能会被中断取消,这将停止计算,如果它检查中断,这不适用于CompletableFuture
,请参阅CompletableFuture.cancel(boolean)
:
Parameters:
参数:
mayInterruptIfRunning - this value has no effect in this implementation because interrupts are not used to control processing.
mayInterruptIfRunning - 此值在此实现中无效,因为中断不用于控制处理。
So when you cancel either, future1
or future2
, successfully, the only immediate effect would be the cancellation of many
, which you can also achieve by calling cancel
on many
itself. 因此,当您成功取消
future1
或future2
,唯一的直接影响是取消many
,您也可以通过调用many
本身cancel
来实现。 It would have a broader effect, if there were more dependent stages, but since you stated, that you don't want to keep references to future1
or future2
, this doesn't seem to be the case. 如果有更多的依赖阶段会产生更广泛的影响,但是既然你说过,你不想保留对
future1
或future2
引用, future1
情况似乎并非如此。
The following code demonstrates the behavior: 以下代码演示了该行为:
CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
System.out.println("supplying value");
return "foo";
});
CompletableFuture<String> then = supply.thenApply(s -> {
System.out.println("Evaluating next stage");
return s;
});
CompletableFuture<?> last = then.handle((s,t) -> {
System.out.println("last stage: value: "+s+", throwable: "+t);
return "";
});
System.out.println("cancelling: "+supply.cancel(true));
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
This code reproducible prints: 此代码可重现打印:
last stage: value: null, throwable: java.util.concurrent.CompletionException: java.util.concurrent.CancellationException
canceling: true
supplying value
(the order might change) (订单可能会改变)
regardless of whether you call supply.cancel(true)
or then.cancel(true)
or whether you pass true
or false
; 无论你是调用
supply.cancel(true)
还是then.cancel(true)
还是调用true
或false
; it won't stop the ongoing Supplier
evaluation. 它不会阻止正在进行的
Supplier
评估。
There will be a difference, if the associated computation hasn't been started yet and it does check the cancellation state when starting, like with the actions produced by the convenience methods in CompletableFuture
. 如果相关计算尚未开始并且它在启动时检查取消状态,则会有所不同,就像
CompletableFuture
的便捷方法所产生的操作一样。 This is a rare situation, as normally, your service.request(paramN)
call is supposed to trigger the evaluation. 这是一种罕见的情况,通常,您的
service.request(paramN)
调用应该触发评估。
It's a fundamental property of the CompletableFuture
, as its name suggests, that it is completable , ie anyone could call complete
on it, thus, the CompletableFuture
can't control whoever might eventually call complete
on it in the future. 这是一个基本属性
CompletableFuture
,顾名思义,它是completable,即任何人都可以称之为complete
就可以了,因此, CompletableFuture
无法控制谁可能最终会调用complete
它的未来。 So all, cancel
can achieve, is to set it to the cancelled state, which implies ignoring subsequent completion attempts and propagating the cancellation downward to the dependent actions. 因此,
cancel
可以实现的是将其设置为取消状态,这意味着忽略后续完成尝试并将取消向下传播到从属动作。
So the bottom line is that you might already be fine with just calling cancel
on the many
instance, because calling cancel
on future1
and future2
is unlikely to have an effect that is worth the complication of your code. 因此,底线是,你可能已经罚款只是打电话
cancel
在many
情况下,因为调用cancel
对future1
和future2
不太可能,是值得您的代码的复杂效果。
The tree constructed by CompletableFuture.allOf
doesn't hold any references to the given instances of CompletableFuture
. 由
CompletableFuture.allOf
构造的树不包含对CompletableFuture
的给定实例的任何引用。 Instead if just builds completion tree, which is is completed when all of the given CompletableFutures complete (from JavaDocs ). 相反,如果只是构建完成树,则在所有给定的CompletableFutures完成时完成 (来自JavaDocs )。
So probably you have to keep references to all CompletableFuture
in order to cancel them sequentially when it is needed. 因此,您可能必须保留对所有
CompletableFuture
引用,以便在需要时按顺序取消它们。
You can give a try for my library: https://github.com/vsilaev/tascalate-concurrent 您可以尝试我的库: https : //github.com/vsilaev/tascalate-concurrent
It provides both truly cancelable CompletionStage implementations (CompletableTask) as well as methods to combine them (Promises.all) 它提供了真正可取消的CompletionStage实现(CompletableTask)以及组合它们的方法(Promises.all)
CompletionStage<Something> future1 = CompletableTask
.complete(param1, myExecutor).thenApplyAsync(this::serviceCall);
CompletionStage<Something> future2 = CompletableTask
.complete(param2, myExecutor).thenApplyAsync(this::serviceCall);
Promise<List<Something>> many = Promises.all(future1, future2);
Now you can call many.cancel(true)
and both future1
and future2
will be cancelled (if not yet completed). 现在您可以调用
many.cancel(true)
, future1
和future2
都将被取消(如果尚未完成)。 Moreover, if either of individual futures completes exceptionally, then another one will be cancelled automatically (again, if not yet completed). 此外,如果个别期货中的任何一个异常完成,则另一个将自动取消(再次,如果尚未完成)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.