简体   繁体   English

来自Java Runnable的Py4J回调

[英]Py4J callback from Java Runnable

I'm currently trying to do the following using Py4J: 我目前正在尝试使用Py4J执行以下操作:

  • Define a method ("executor") in Python which calls JVM methods 在Python中定义一个调用JVM方法的方法(“executor”)
  • Define a Python ("callback") object implementing a JVM interface 定义实现JVM接口的Python(“回调”)对象
  • Construct a JVM object given this callback object 在给定此回调对象的情况下构造JVM对象
  • Call a method on this object which will spawn a new thread in Java, call the callback on the callback object, which will (on the Python side) execute the "executor" method 在这个对象上调用一个方法,它将在Java中生成一个新线程,在回调对象上调用回调,这将(在Python端)执行“executor”方法

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: 结果很简单:

  1. Use ClientServer on the Python side and on the Java side as well! 在Python端和Java端使用ClientServer!
  2. Don't call gateway.shutdown() as this will disconnect Python before the callback can be received (duh!) 不要调用gateway.shutdown(),因为这会在收到回调之前断开Python(呃!)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM