[英]Py4J callback from Java Runnable
I'm currently trying to do the following using Py4J: 我目前正在尝试使用Py4J执行以下操作:
Here's what I have on the Java side of things: 这就是我在Java方面所拥有的东西:
package javabridge.test;
public interface PythonCallback {
Object notify(Object source);
}
package javabridge.test;
public class ScheduledRunnable implements Runnable {
private PythonCallback callback;
public ScheduledRunnable(PythonCallback callback) {
this.callback = callback;
}
@Override
public void run() {
System.out.println("[ScheduledRunnable] run -> notify");
this.callback.notify(this);
}
}
package javabridge.test;
import py4j.GatewayServer;
public class Test {
private PythonCallback callback;
public Test(PythonCallback callback) {
this.callback = callback;
}
public void runSynchronous() {
System.out.println("[runSynchronous] run -> notify");
this.callback.notify(this);
}
public void runAsynchronous() {
System.out.println("[runAsynchronous] run -> spawn thread");
ScheduledRunnable runnable = new ScheduledRunnable(callback);
Thread t = new Thread(runnable);
t.start();
}
public static void main(String[] args) {
GatewayServer server = new GatewayServer();
server.start(true);
}
}
On the Python side, I have the following script: 在Python方面,我有以下脚本:
from py4j.java_gateway import JavaGateway, java_import, get_field, CallbackServerParameters
from py4j.clientserver import ClientServer, JavaParameters, PythonParameters
gateway = JavaGateway(callback_server_parameters=CallbackServerParameters())
#gateway = ClientServer(java_parameters=JavaParameters(), python_parameters=PythonParameters())
java_import(gateway.jvm, 'javabridge.test.*')
class PythonCallbackImpl(object):
def __init__(self, execfunc):
self.execfunc = execfunc
def notify(self, obj):
print('[PythonCallbackImpl] notified from Java')
self.execfunc()
return 'dummy return value'
class Java:
implements = ["javabridge.test.PythonCallback"]
def simple_fun():
print('[simple_fun] called')
gateway.jvm.System.out.println("[simple_fun] Hello from python!")
# Test 1: Without threading
input('Ready to begin test 1')
python_callback = PythonCallbackImpl(simple_fun)
nothread_executor = gateway.jvm.Test(python_callback)
nothread_executor.runSynchronous()
# Test 2: With threading
input('Ready to begin test 2')
python_callback = PythonCallbackImpl(simple_fun)
nothread_executor = gateway.jvm.Test(python_callback)
nothread_executor.runAsynchronous()
gateway.shutdown()
Here's what happens when trying to execute this script. 这是尝试执行此脚本时发生的情况。 First, using
gateway = ClientServer(java_parameters=JavaParameters(), python_parameters=PythonParameters())
, both tests fail: 首先,使用
gateway = ClientServer(java_parameters=JavaParameters(), python_parameters=PythonParameters())
,两个测试都失败:
Test 1:
py4j.protocol.Py4JJavaError: An error occurred while calling o0.runSynchronous.
: py4j.Py4JException: Command Part is Empty or is the End of Command Part
at py4j.Protocol.getObject(Protocol.java:277)
at py4j.Protocol.getReturnValue(Protocol.java:458)
Test 2:
Exception in thread "Thread-4" py4j.Py4JException: Error while obtaining a new communication channel
at py4j.CallbackClient.getConnectionLock(CallbackClient.java:218)
at py4j.CallbackClient.sendCommand(CallbackClient.java:337)
at py4j.CallbackClient.sendCommand(CallbackClient.java:316)
However, if I comment out the self.execfunc()
line, test 1 does work without raising errors. 但是,如果我注释掉
self.execfunc()
行,则测试1可以正常工作而不会引发错误。 Test 2 still fails however: 然而,测试2仍然失败:
Exception in thread "Thread-5" py4j.Py4JException: Error while sending a command.
at py4j.CallbackClient.sendCommand(CallbackClient.java:357)
at py4j.CallbackClient.sendCommand(CallbackClient.java:316)
Now switching to gateway = JavaGateway(callback_server_parameters=CallbackServerParameters())
. 现在切换到
gateway = JavaGateway(callback_server_parameters=CallbackServerParameters())
。 When I keep self.execfunc()
commented out, test 2 still fails here: 当我将
self.execfunc()
注释掉时,测试2仍然在这里失败:
Exception in thread "Thread-5" py4j.Py4JException: Error while sending a command.
at py4j.CallbackClient.sendCommand(CallbackClient.java:357)
at py4j.CallbackClient.sendCommand(CallbackClient.java:316)
But at least test 1 does work with self.execfunc()
enabled. 但至少测试1确实可以使用
self.execfunc()
。
My question is: how can I use the threaded approach with the self.execfunc()
call? 我的问题是:如何在
self.execfunc()
调用中使用线程方法? Is this possible with Py4J? 这可能与Py4J有关吗?
Edit: and to make things even more tricky, Java commands called by self.execfunc()
should run in the same Java thread that invoked .notify()
. 编辑:为了使事情变得更加棘手,
self.execfunc()
调用的Java命令应该在调用.notify()
的同一Java线程中运行。
Solved. 解决了。 Turns out to be very simple:
结果很简单:
Java will then neatly adhere to the expected thread model, ie Java commands called by the receiving Python callback are executed in the same Java thread that performed the callback. 然后,Java将完全遵循预期的线程模型,即接收Python回调调用的Java命令在执行回调的同一Java线程中执行。
Through a simple Python function, a shutdown_when_done
method can be added which waits until all callbacks have come back before quitting. 通过一个简单的Python函数,可以添加
shutdown_when_done
方法,该方法等待所有回调在退出之前返回。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.