我正在寻找Python的yield from
或gevent的猴子补丁的Java / Akka等价物。
更新
关于问题是什么的问题存在一些混乱,所以让我重申一下这个问题:
如果我有未来,我如何在不阻止线程的情况下等待未来竞争而不会在未来完成之前返回来电?
让我们说我们有阻止的方法:
public Object foo() {
Object result = someBlockingCall();
return doSomeThingWithResult(result);
}
为了使这个异步,我们将传递SomeBlockingCall()一个回调:
public void foo() {
someAsyncCall(new Handler() {
public void onSuccess(Object result) {
message = doSomethingWithResult(result);
callerRef.tell(message, ActorRef.noSender());
}
});
}
对foo()
的调用现在在结果准备好之前返回,因此调用者不再获得结果。我们必须通过传递消息将结果返回给调用者。要将同步代码转换为异步Akka代码,需要重新设计调用者。
我想要的是看起来像Python的Gevent等同步代码的异步代码。
我想写:
public Object foo() {
Future future = someAsyncCall();
// save stack frame, go back to the event loop and relinquish CPU
// so other events can use the thread,
// and come back when future is complete and restore stack frame
return yield(future);
}
这样我就可以在没有重新设计的情况下使我的同步代码异步。
这是可能的吗?
注意:
Play框架似乎是fake this async()
和AsyncResult
。但是这一般不会起作用,因为我必须编写处理AsyncResult
的代码,它看起来像上面的回调处理程序。
答案 0 :(得分:3)
我认为尝试恢复更直接的同步设计,虽然高效,但实际上是一个好主意和好主意(例如参见here)。
Quasar可以从异步API中获取仍然高效的同步/阻止API (请参阅此blog post),这看起来正是您要查找的内容
基本问题是不同步/阻塞样式本身是坏的(实际上异步和同步是双重样式,可以相互转换,例如参见here),而不是阻止Java的重量级线程效率不高:它不是一个抽象问题而是一个实现问题,所以不要放弃更容易的线程抽象而只是因为实现是效率低下,我同意代码的未来更好地尝试寻找更有效的线程实现。
正如Roland暗示的那样,Quasar向JVM添加了轻量级线程或光纤,因此您可以获得与异步框架相同的性能,而无需放弃线程抽象和常规命令控制语言中可用的流构造(序列,循环等)。
它还将JVM / JDK的线程及其光纤统一在一个通用的 strand 接口下,因此它们可以无缝地互操作,并提供java.util.concurrent
的移植这个统一的概念。
在股票之上(光纤或常规线程)Quasar还提供成熟的 Erlang风格的演员,阻止Go-like频道和数据流编程,因此您可以选择最适合您的技能和需求的并发编程范例,而不必被强制插入。
它还为流行和标准技术提供绑定(作为Comsat项目的一部分),因此您可以保留代码资产,因为移植工作将是最小的(如果有的话)。出于同样的原因,您也可以选择退出,如果您选择。
目前,Quasar已经绑定了Pulsar项目下的 Java 7和8 , Clojure ,很快JetBrain's Kotlin。基于JVM字节码检测,如果存在集成模块,Quasar可以使用任何JVM语言,并且它提供了构建其他模块的工具。
答案 1 :(得分:1)
你的问题的答案是“不”,这非常符合设计。将方法编写为异步意味着返回Future
作为结果,方法本身不会执行计算,但会安排稍后提供结果。然后,您可以将此Future
传递到进一步使用它的正确位置,例如使用众多组合器中的一个(例如map
,recover
等)进行转换。 / p>
无论您使用哪种技术,等待Future的严格结果都必须阻止当前的执行线程。使用普通的JVM线程,您将阻止来自操作系统的真实线程,使用Quasar,您将阻止您的光纤,使用Akka,您将阻止您的Actor(*);阻止意味着阻止,没有办法解决这个问题。
(*)在演员中,您将在稍后通过消息获得结果,并且在此之前您必须切换行为,以便新传入的消息被隐藏,拒绝或丢弃,具体取决于您的使用 - 情况下。