2009-08-11 10 views
10

Estoy construyendo un marco en Java que escuchará eventos y luego los procesará en Jython. Se enviarán diferentes tipos de eventos a diferentes scripts.¿Cómo se ejecutan guiones jython multiproceso desde Java?

Dado que jython tarda bastante tiempo en compilar la secuencia de comandos cuando se llama a PythonInterpreter.exec(), tendré que precompilar las secuencias de comandos. Lo estoy haciendo la siguiente manera:

// 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"); 

El objeto PyCode compilado sería empujado al repositorio y se utiliza como eventos vienen en

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

Ahora para mi dilema - que podría suceder que hay múltiples eventos de cierto tipo que suceden al mismo tiempo; por lo tanto, varias instancias de script se ejecutan al mismo tiempo.

Casi todas las secuencias de comandos probablemente seguirán siendo efímeras: hasta 100 líneas, sin bucles. El número y la frecuencia son completamente aleatorios (eventos generados por el usuario) y pueden ser de 0 a aproximadamente 200 por segundo por tipo de evento.

¿Cuál sería la mejor manera de hacerlo? Estoy viendo algunas posibilidades:

  1. uso de sincronización en el punto de evento de disparo - esto impediría varias instancias del mismo guión, sino también eventos no sería procesado tan pronto como deberían ser
  2. crear una agrupación del mismo tipo de scripts poblados de alguna manera al clonar el objeto PyCode original - el mayor problema sería optimizar los tamaños del grupo
  3. clonar dinámicamente el objeto del script del padre siempre que sea necesario y luego descartarlo cuando exec() finalice - de esta forma se elimina el retraso de compilar pero todavía está presente en el método de clonación

Probablemente la combinación de los números 2 y 3 sería la mejor: ¿creará tamaños de grupo dinámicos?

¿Alguna idea? ;)

Respuesta

3

Es una pena que las instancias PyCode no sean inmutables (hay muchos miembros públicos en las clases).

Puede precompilar un guión reutilizable utilizando este código:

// 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 }); 

A continuación, puede utilizar el constructor para producir casos PyCode para el script siempre que lo necesite:

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

que sería el Primero, admitir que esta no es una buena forma de hacer las cosas y, probablemente, lo dice todo sobre mi inexperiencia con Jython. Sin embargo, es significativamente más rápido que compilar todo el tiempo. El código funciona bajo Jython 2.2.1, pero no se compilará en Jython 2.5 (ni lo hará el tuyo).

+0

¡Guau! Funciona como un amuleto;) Debe tenerse en cuenta que se debe crear una instancia de PythonInterpreter antes de llamar a Module.compile (...). Si no se lanza NullPointerException desde SyspathJavaLoader.loadClass() Has sido de lo más útil. Ahora todo lo que tengo que hacer es integrar esto en un conjunto de scripts dinámicamente redimensionable ... – nEJC

+0

¿Conoces algún equivalente para Jython 2.5? – Laurent

+0

@Laurent - no, no he visto esta área desde que publiqué esta respuesta – McDowell

1

PythonInterpreter es caro, este código usará solo uno.

#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

Interesante, pero hasta donde sé, PythonInterpreter no es seguro para subprocesos, por lo que probablemente no sea una buena idea (al menos para mí) ... . tendrá que hacer algunas pruebas ... – nEJC

+0

Sí, PythonInterpreter no es seguro para subprocesos, esa es exactamente la razón por la que lo he hecho de esta manera. pi.eval ("action.execute") solo le proporciona una instancia del método como un objeto java, no lo ejecuta. – Ron

Cuestiones relacionadas