英文:
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
addCallback
approach may require a thread to block onfuture2.get
untilfuture2
is 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
, yourRunnable
won't run until both input futures are done, sofuture2.get
will not block. -
whenAllComplete
returns 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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论