2009-08-11 3 views
10

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

Поскольку 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 была бы лучшими - создавая динамические размеры пула?

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

ответ

3

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

+0

Nice! Работает как шарм;) Следует отметить, что экземпляр PythonInterpreter ДОЛЖЕН быть создан до вызова модуля Module.compile (...). Если исключение NullPointerException выбрано из SyspathJavaLoader.loadClass() Вы были наиболее полезны. Теперь все, что мне нужно сделать, это интегрировать это в динамически повторно используемый пул скриптов ... – nEJC

+0

Вы знаете какой-либо эквивалент для Jython 2.5? – Laurent

+0

@ Laurent - нет, я не смотрел на эту область, так как я разместил этот ответ – McDowell

1

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 
+0

Интересно, но насколько я знаю, PythonInterpreter не является потокобезопасным, поэтому его, вероятно, не очень хорошая идея (по крайней мере для меня) .. должен будет провести некоторое тестирование, хотя ... – nEJC

+0

Да, PythonInterpreter не является потокобезопасным, и именно поэтому я сделал это таким образом. pi.eval ("action.execute") дает вам экземпляр метода как объект java, не запускает его. – Ron

Смежные вопросы