放弃 thread/CPU 直到异步调用在 Akka 和 Java 中完成?
Relinquish the thread/CPU until async call completes in Akka and Java?
我正在寻找 Java/Akka 相当于 Python 的 yield from
或 gevent 的猴子补丁。
更新
评论中对问题的内容有些混淆,所以让我重述一下问题:
如果我有一个未来,我如何等待未来竞争而不阻塞线程并且在未来完成之前不返回调用者?
假设我们有阻塞的方法:
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());
}
});
}
现在 returns 在结果准备好之前对 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
的代码,它看起来像上面的回调处理程序。
您的问题的答案是“否”,这在很大程度上是设计使然。编写异步方法意味着返回 Future
作为其结果,该方法本身不会执行计算但会安排稍后提供结果。然后,您可以将此 Future
传递到进一步使用它的正确位置,例如通过使用许多组合器之一(如 map
、recover
等)对其进行转换。
无论您使用哪种技术,等待 Future 的严格结果都必须阻塞当前执行线程。使用普通 JVM 线程,您将阻塞操作系统的真实线程,使用 Quasar,您将阻塞您的 Fiber,使用 Akka,您将阻塞您的 Actor (*);堵就是堵,没办法。
(*) 在 Actor 中,您将在稍后通过消息获得结果,并且在此之前,您将不得不切换行为,以便根据您的情况隐藏、拒绝或丢弃新传入的消息用例。
我认为尝试找回更直接的同步设计,虽然 高效 实际上是一个好的意图和一个好主意(例如参见 [=11=] ).
Quasar has facilities to obtain sync/blocking APIs that are still highly efficient from async APIs (see this blog post),这正是您要查找的内容。
根本问题是不是 sync/blocking 样式本身不好(实际上异步和同步是双重样式,可以相互转化,例如here),而不是阻塞Java的重量级线程效率不高:这不是抽象问题而是实现问题,所以与其仅仅因为实现效率低下而放弃更简单的线程抽象,我同意你的代码的未来最好尝试寻找更高效的线程实现。
正如 Roland 暗示的那样,Quasar 向 JVM 添加了轻量级线程或 fibers,因此您可以获得与异步框架相同的性能 而无需放弃线程抽象 和语言中可用的常规命令式控制流结构(序列、循环等)。
它还将JVM/JDK的线程及其纤程统一在一个通用的strand接口下,因此它们可以无缝互操作,并提供 java.util.concurrent
到这个统一概念的移植。
在 strands(纤维或常规线程)之上,Quasar 还提供成熟的 Erlang 风格的 actor,阻塞类似 Go 的通道 和 dataflow 编程,因此您可以选择最适合您的技能和需求的并发编程范例,而不必被迫选择一个。
它还提供流行和标准技术的绑定(作为Comsat项目的一部分),因此您可以保留您的代码资产 因为移植工作将是最小的(如果有的话)。出于同样的原因,如果您愿意,您也可以轻松选择退出。
目前 Quasar 在 Pulsar project and soon JetBrain's Kotlin 下绑定了 Java 7 和 8、Clojure。基于 JVM 字节码工具,如果存在集成模块,Quasar 可以真正使用任何 JVM 语言,并且它提供了构建其他模块的工具。
我正在寻找 Java/Akka 相当于 Python 的 yield from
或 gevent 的猴子补丁。
更新
评论中对问题的内容有些混淆,所以让我重述一下问题:
如果我有一个未来,我如何等待未来竞争而不阻塞线程并且在未来完成之前不返回调用者?
假设我们有阻塞的方法:
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());
}
});
}
现在 returns 在结果准备好之前对 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
的代码,它看起来像上面的回调处理程序。
您的问题的答案是“否”,这在很大程度上是设计使然。编写异步方法意味着返回 Future
作为其结果,该方法本身不会执行计算但会安排稍后提供结果。然后,您可以将此 Future
传递到进一步使用它的正确位置,例如通过使用许多组合器之一(如 map
、recover
等)对其进行转换。
无论您使用哪种技术,等待 Future 的严格结果都必须阻塞当前执行线程。使用普通 JVM 线程,您将阻塞操作系统的真实线程,使用 Quasar,您将阻塞您的 Fiber,使用 Akka,您将阻塞您的 Actor (*);堵就是堵,没办法。
(*) 在 Actor 中,您将在稍后通过消息获得结果,并且在此之前,您将不得不切换行为,以便根据您的情况隐藏、拒绝或丢弃新传入的消息用例。
我认为尝试找回更直接的同步设计,虽然 高效 实际上是一个好的意图和一个好主意(例如参见 [=11=] ).
Quasar has facilities to obtain sync/blocking APIs that are still highly efficient from async APIs (see this blog post),这正是您要查找的内容。
根本问题是不是 sync/blocking 样式本身不好(实际上异步和同步是双重样式,可以相互转化,例如here),而不是阻塞Java的重量级线程效率不高:这不是抽象问题而是实现问题,所以与其仅仅因为实现效率低下而放弃更简单的线程抽象,我同意你的代码的未来最好尝试寻找更高效的线程实现。
正如 Roland 暗示的那样,Quasar 向 JVM 添加了轻量级线程或 fibers,因此您可以获得与异步框架相同的性能 而无需放弃线程抽象 和语言中可用的常规命令式控制流结构(序列、循环等)。
它还将JVM/JDK的线程及其纤程统一在一个通用的strand接口下,因此它们可以无缝互操作,并提供 java.util.concurrent
到这个统一概念的移植。
在 strands(纤维或常规线程)之上,Quasar 还提供成熟的 Erlang 风格的 actor,阻塞类似 Go 的通道 和 dataflow 编程,因此您可以选择最适合您的技能和需求的并发编程范例,而不必被迫选择一个。
它还提供流行和标准技术的绑定(作为Comsat项目的一部分),因此您可以保留您的代码资产 因为移植工作将是最小的(如果有的话)。出于同样的原因,如果您愿意,您也可以轻松选择退出。
目前 Quasar 在 Pulsar project and soon JetBrain's Kotlin 下绑定了 Java 7 和 8、Clojure。基于 JVM 字节码工具,如果存在集成模块,Quasar 可以真正使用任何 JVM 语言,并且它提供了构建其他模块的工具。