Как многопоточные скрипты Jython, работающие с Java? - PullRequest
10 голосов
/ 11 августа 2009

Я создаю фреймворк на Java, который будет прослушивать события, а затем обрабатывать их в Jython. Различные типы событий будут отправлены в разные сценарии.

Поскольку для компиляции скрипта jython требуется некоторое время, когда вызывается PythonInterpreter.exec (), мне придется предварительно скомпилировать скрипты. Я делаю это следующим образом:

// 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 - создание динамических размеров пула?

Итак, есть мысли? ;)

Ответы [ 2 ]

3 голосов
/ 13 августа 2009

Жаль, что 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 (и не будет вашим).

1 голос
/ 27 ноября 2009

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
...