【问题标题】:Howto multithreaded jython scripts running from java?如何从java运行多线程python脚本?
【发布时间】:2010-11-18 15:40:14
【问题描述】:

我正在用 Java 构建一个框架,它将监听事件,然后在 Jython 中处理它们。不同的事件类型将被发送到不同的脚本。

由于调用 PythonInterpreter.exec() 时 jython 需要相当长的时间来编译脚本,所以我必须预编译脚本。我正在这样做:

// initialize the script as string (would load it from file in final version)
String script = "print 'foo'";
// get the compiled code object
PyCode compiled = org.python.core.__builtin__.compile( script, "<>", "exec" );

PyCode 编译的对象将被推送到存储库并在事件进入时使用

PythonInterpreter pi = new PythonInterpreter();
pi.set( "variable_1", "value_1");
pi.set( "variable_x", "value_x");
pi.exec( compiled );

现在解决我的难题 - 可能会同时发生多个特定类型的事件 - 因此多个脚本实例同时运行。

几乎所有脚本都可能保持短暂的生命周期——最多 100 行,没有循环。数量和频率是完全随机的(用户生成的事件),每种事件类型可能是每秒 0 到大约 200 个。

最好的方法是什么?我正在研究几种可能性:

  1. 在触发事件点使用同步 - 这将防止同一脚本的多个实例,但事件也不会像应有的那样快速处理
  2. 创建一个通过克隆原始 PyCode 对象以某种方式填充的相同类型脚本池 - 最大的问题可能是优化池大小
  3. 在需要时从父对象动态克隆脚本对象,然后在 exec() 完成时将其丢弃 - 这样从编译中删除了滞后,但它仍然存在于克隆方法中

数字 2 和 3 的组合可能是最好的 - 创建动态池大小?

那么,有什么想法吗? ;)

【问题讨论】:

    标签: java multithreading thread-safety jython


    【解决方案1】:

    很遗憾PyCode 实例不是不可变的(类中有很多公共成员)。

    您可以使用以下代码预编译可重用脚本:

    // TODO: generate this name
    final String name = "X";
    byte[] scriptBytes = PyString.to_bytes(script);
    CompilerFlags flags = Py.getCompilerFlags();
    ByteArrayOutputStream ostream = new ByteArrayOutputStream();
    Module.compile(parser.parse(new ByteArrayInputStream(scriptBytes), "exec",
        "<>", flags), ostream, name, "<>", false, false, false, flags);
    byte[] buffer = ostream.toByteArray();
    Class<PyRunnable> clazz = BytecodeLoader.makeClass(name, null, buffer);
    final Constructor<PyRunnable> constructor = clazz
        .getConstructor(new Class[] { String.class });
    

    然后,您可以在需要时使用构造函数为脚本生成 PyCode 实例:

     PyRunnable r = constructor.newInstance(name);
     PyCode pc = r.getMain();
    

    我会第一个承认这不是一种好的做事方式,并且可能充分说明了我对 Jython 的缺乏经验。但是,它比每次编译都要快得多。该代码可在 Jython 2.2.1 下运行,但无法在 Jython 2.5 下编译(您的也不行)。

    【讨论】:

    • 不错!像魅力一样工作;) 应该注意的是,必须在调用第一个 Module.compile(...) 之前创建 PythonInterpreter 的实例。如果不是从 SyspathJavaLoader.loadClass() 抛出 NullPointerException 你是最有帮助的。现在我所要做的就是将它集成到一个可动态调整大小的脚本池中......
    • 你知道 Jython 2.5 的任何等价物吗?
    • @Laurent - 不,自从我发布这个答案后,我没有看过这个区域
    【解决方案2】:

    PythonInterpreter 很昂贵,这段代码只会用到一个。

    #action.py
    def execute(filename, action_locals):
        #add caching of compiled scripts here
        exec(compile(open(filename).read(), filename, 'exec'), action_locals)
    
    //class variable, only one interpreter
    PythonInterpreter pi;
    
    //run once in init() or constructor
    pi = new PythonInterpreter();//could do more initialization here
    pi.exec("import action");
    
    //every script execution
    PyObject pyActionRunner = pi.eval("action.execute");
    PyString pyActionName = new PyString(script_path);
    PyDictionary pyActionLocals = new PyDictionary();
    pyActionLocals.put("variable_1", "value_1");
    pyActionLocals.put("variable_x", "value_x")
    pyActionRunner.__call__(pyActionName, pyActionLocals);
    
    #example_script.py
    print variable_1, variable_x
    

    【讨论】:

    • 很有趣,但据我所知 PythonInterpreter 不是线程安全的,所以它可能不是一个好主意(至少对我来说)......虽然需要做一些测试......
    • 是的,PythonInterpreter 不是线程安全的,这正是我这样做的原因。 pi.eval("action.execute") 只给你一个作为java对象的方法实例,不运行它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 2019-02-27
    • 1970-01-01
    • 2020-10-08
    • 2021-01-27
    相关资源
    最近更新 更多