我运行的异步任务很少,我需要等到其中至少一个完成(将来我可能需要等待 util M out of 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
有这样的吗?或任何类似的东西,对 Future 来说不是必需的。目前我循环收集期货,检查一个是否完成,然后休眠一段时间并再次检查。这看起来不是最好的解决方案,因为如果我长时间睡眠,则会增加不必要的延迟,如果我睡眠时间很短,则会影响性能。
我可以尝试使用
new CountDownLatch(1)
并在任务完成时减少倒计时并执行
countdown.await()
,但我发现只有在我控制未来创建时才有可能。这是可能的,但需要重新设计系统,因为当前创建任务的逻辑(向 ExecutorService 发送 Callable)与等待哪个 Future 的决策是分开的。我也可以覆盖
<T> RunnableFuture<T> AbstractExecutorService.newTaskFor(Callable<T> callable)
并创建 RunnableFuture 的自定义实现,能够附加侦听器以在任务完成时收到通知,然后将此类侦听器附加到所需的任务并使用 CountDownLatch,但这意味着我必须为我使用的每个 ExecutorService 覆盖 newTaskFor - 并且可能会有实现它不扩展 AbstractExecutorService。我也可以尝试包装给定的 ExecutorService 用于相同的目的,但是我必须装饰所有产生 Futures 的方法。
所有这些解决方案都可能有效,但看起来非常不自然。看起来我错过了一些简单的东西,比如
WaitHandle.WaitAny(WaitHandle[] waitHandles)
在 C# 中。这类问题有没有众所周知的解决方案?
更新:
最初我根本无法访问 Future 创建,因此没有优雅的解决方案。重新设计系统后,我可以访问 Future 创建并能够将 countDownLatch.countdown() 添加到执行过程中,然后我可以 countDownLatch.await() 并且一切正常。感谢其他答案,我不知道 ExecutorCompletionService ,它确实可以在类似的任务中有所帮助,但在这种特殊情况下,它无法使用,因为某些 Futures 是在没有任何执行程序的情况下创建的 - 实际任务通过网络发送到另一台服务器,远程完成并收到完成通知。