等到任何一个 Future<T> 完成

2022-08-31 17:36:55

我运行的几个异步任务,我需要等到其中至少一个完成(将来我可能需要等待Util M的N个任务完成)。目前它们被呈现为未来,所以我需要类似的东西

/**
 * Blocks current thread until one of specified futures is done and returns it. 
 */
public static <T> Future<T> waitForAny(Collection<Future<T>> futures) 
        throws AllFuturesFailedException

有这样的事情吗?或者任何类似的东西,对于未来来说不是必需的。目前,我循环收集期货,检查一个是否完成,然后睡一段时间,然后再次检查。这看起来不是最好的解决方案,因为如果我长时间睡眠,那么就会添加不必要的延迟,如果我睡得很短,那么它会影响性能。

我可以尝试使用

new CountDownLatch(1)

并在任务完成时减少倒计时并执行

countdown.await()

,但我发现只有当我控制未来创造时才有可能。这是可能的,但需要重新设计系统,因为目前任务创建(将可调用发送到执行器服务)的逻辑与等待哪个未来的决策是分开的。我也可以覆盖

<T> RunnableFuture<T> AbstractExecutorService.newTaskFor(Callable<T> callable)

并创建RunnableFuture的自定义实现,能够附加侦听器以在任务完成时收到通知,然后将此类侦听器附加到所需的任务并使用CountDownLatch,但这意味着我必须为我使用的每个执行器服务覆盖newTaskFor - 并且可能会有不扩展 AbstractExecutorService 的实现。我也可以尝试出于相同的目的包装给定的执行器服务,但随后我必须装饰所有生成期货的方法。

所有这些解决方案可能有效,但似乎非常不自然。看起来我错过了一些简单的东西,比如

WaitHandle.WaitAny(WaitHandle[] waitHandles)

在 c# 中。对于此类问题,是否有任何众所周知的解决方案?

更新:

最初我根本没有机会访问Future创作,因此没有优雅的解决方案。重新设计系统后,我可以访问Future creation,并能够将countDownLatch.countdown()添加到执行过程中,然后我可以countDownLatch.await()并且一切正常。感谢其他答案,我不知道执行器CompletionService,它确实可以在类似的任务中有所帮助,但是在这种特殊情况下,它无法使用,因为一些期货是在没有任何执行器的情况下创建的 - 实际任务通过网络发送到另一台服务器,远程完成并收到完成通知。


答案 1

简单,请查看执行器完成服务


推荐