英文:
Is nesting of futures an anti pattern?
问题
我有一个正在创建的ListenableFuture,如下所示:
ListenableFuture<X> future1 = ...
ListenableFuture<X> future2 = ...
Futures.addCallback(future1, futureCallback, executor);
现在在futureCallback内部我这样做:
public void onSuccess(Object result) {
Object f2 = future2.get();
.
.
. <对 f2 做一些操作>
}
在回调中进行.get()调用是一件不好的事情吗?有人告诉我这样可能会导致程序挂起。这是真的吗?为什么会发生这种情况?
有没有更好的方法来实现相同的功能?
英文:
I have a ListenableFuture that I am creating like:
ListenableFuture<X> future1 = ...
ListenableFuture<X> future2 = ...
Futures.addCallback(future1, futureCallback, executor);
Now inside the futureCallback I do :
public void onSuccess(Object result) {
Object f2 = future2.get();
.
.
. <do something with f2>
}
Is it a bad thing to do a .get() call in a callback? I was told that I could get into a hung up state. Is that true and why would it so happen?
What is the alternative of achieving the same thing in a better way?
答案1
得分: 1
你可能更希望使用类似这样的方式:
whenAllSucceed(future1, future2).run(...)。
你传递给 run 的 Runnable 仍然需要调用 future2.get。不过,whenAllComplete 在某些方面优于 addCallback 方法:
-
addCallback方法可能需要一个线程在future2.get上阻塞,直到future2完成。(这至少会浪费一些资源。如果线程用尽,你的代码可能会挂起。或者你可能尝试创建新线程并耗尽内存。)使用whenAllComplete,你的Runnable不会在两个输入 future 都完成之前运行,因此future2.get不会阻塞。 -
whenAllComplete返回一个Future。你可以使用它来检查错误。如果你取消它,它会同时取消两个输入 future。
另请注意,如果多个输入失败,whenAllSucceed 会记录日志。这并不总是理想的。如果你不想要这种日志记录,你可能更喜欢 whenAllComplete。(希望将来我们会提供关闭日志记录的方法。)
英文:
You probably want something more like:
whenAllSucceed(future1, future2).run(...)
The Runnable you pass to run will still have to call future2.get. Still, whenAllComplete has some advantages over the addCallback approach:
-
The
addCallbackapproach may require a thread to block onfuture2.getuntilfuture2is done. (This is at least a small waste of resources. If you run out of threads, your code may hang. Or you might try to create a new thread and run out of memory.) WithwhenAllComplete, yourRunnablewon't run until both input futures are done, sofuture2.getwill not block. -
whenAllCompletereturns aFuture. You can use this to check for errors. And if you cancel it, it will cancel both input futures.
Note also that whenAllSucceed will log if multiple inputs fail. This isn't always desirable. If you don't want the logging, you might prefer whenAllComplete. (Hopefully someday we will provide a way to turn off the logging.)
答案2
得分: 0
在回调函数中进行.get()调用是非常常见的做法 - 这通常是你大部分时间想要做的 - 但更标准的模式是使用 transform 或者 transformAsync,以传入一个直接接受结果的函数。
英文:
Doing a .get() call in a callback is outright usual -- it's what you want to do most of the time -- but a more standard pattern is to, instead, use transform or transformAsync to pass in a function that takes the result directly.
答案3
得分: 0
在回调中执行阻塞操作(例如,future.get)会破坏异步编程的思想,将异步程序转变为普通的多线程程序。因此,这确实是一种反模式。
然而,如果你确定不会阻塞,调用get()是可以的。
如果你想要一个等待两个异步结果的回调,你可以使用CompletableFuture:
CompletableFuture<X> future1 = ...
CompletableFuture<X> future2 = ...
CompletableFuture future3 = future1.thenCombineAsync(future2, futureCallback, executor);
英文:
Doing blocking operation in callback (e.g. future.get) destroys the idea of asynchronous programming, turning asynchronous program in ordinary multithreaded. So this is indeed an antipattern.
It's OK, however, to call get() if you are sure it would not block.
If you want a callback which waits for 2 asynchronous results, you can use CompletableFuture instead:
CompletableFuture<X> future1 = ...
CompletableFuture<X> future2 = ...
CompletableFuture future3 = future1.thenCombineAsync(future 2, futureCallback, executor);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论