简体   繁体   English

递归取消allOf CompletableFuture

[英]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? future1future2被取消吗? 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 . 我不愿意坚持future1future2 ,只是为了能够在取消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. 因此,当您成功取消future1future2 ,唯一的直接影响是取消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. 如果有更多的依赖阶段会产生更广泛的影响,但是既然你说过,你不想保留对future1future2引用, 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)还是调用truefalse ; 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. 因此,底线是,你可能已经罚款只是打电话cancelmany情况下,因为调用cancelfuture1future2不太可能,是值得您的代码的复杂效果。

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)future1future2都将被取消(如果尚未完成)。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM