我有一些异步任务正在运行,我需要等待至少其中一个完成(将来可能需要等待M个任务中的N个完成)。 目前它们被表示为Future,所以我需要类似于以下内容:
/**
* 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。目前,我循环遍历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是在没有任何执行器的情况下创建的 - 实际任务通过网络发送到另一个服务器完成,然后接收完成通知。
ExecutorCompletionService
和ExecutorService.invokeAny()
。 - Robert Tupelo-Schneck