2009-08-11 9 views
10

Sto costruendo un framework in Java che ascolterà gli eventi e li elaborerà in Jython. Diversi tipi di eventi verranno inviati a diversi script.Come script jython multithreading in esecuzione da java?

Poiché jython richiede un po 'di tempo per compilare lo script quando viene chiamato PythonInterpreter.exec(), dovrò precompilare gli script. Lo sto facendo in questo modo:

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

L'oggetto PyCode compilato sarebbe spinto a repository e utilizzato come eventi sono disponibili in

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

Ora per la mia enigma - può succedere che ci sono più eventi di un certo tipo che si verificano nello stesso momento - quindi più istanze di script eseguite nello stesso momento.

Quasi tutti gli script probabilmente rimarranno di breve durata - fino a 100 righe, senza loop. Il numero e la frequenza sono completamente casuali (eventi generati dall'utente) e potrebbero variare da 0 a circa 200 al secondo per tipo di evento.

Quale sarebbe il modo migliore per farlo? Sto guardando un paio di possibilità: la sincronizzazione

  1. uso al punto di trigger event - ciò impedirebbe più istanze dello stesso script, ma anche eventi non sarebbe stato elaborato nel più breve tempo dovrebbero essere
  2. creare un pool dello stesso tipo di script popolato in qualche modo dalla clonazione dell'oggetto PyCode originale - il problema più grande probabilmente sarebbe l'ottimizzazione delle dimensioni del pool
  3. clonare dinamicamente l'oggetto script dal genitore ogni volta che è necessario e quindi scartarlo quando exec() termina - in questo modo viene rimosso il lag da compilare ma è ancora presente nel metodo clone

Probabilmente la combinazione dei numeri 2 e 3 sarebbe la migliore - creazione di dimensioni di piscine dinamiche?

Quindi, qualche idea? ;)

risposta

3

È un peccato che le istanze PyCode non siano immutabili (ci sono molti membri pubblici nelle classi).

È possibile precompilare uno script riutilizzabili utilizzando questo codice:

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

È quindi possibile utilizzare il costruttore per la produzione di istanze PyCode per lo script ogni volta che avete bisogno di uno:

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

sarei il prima di ammettere che questo non è un buon modo di fare le cose e probabilmente parla della mia inesperienza con Jython. Tuttavia, è significativamente più veloce della compilazione ogni volta. Il codice funziona con Jython 2.2.1, ma non verrà compilato con Jython 2.5 (né il tuo).

+0

Bello! Funziona come un fascino;) Si noti che un'istanza di PythonInterpreter DEVE essere creata prima che venga chiamato Module.compile (...). Se non NullPointerException viene generato da SyspathJavaLoader.loadClass() Sei stato molto utile. Ora tutto ciò che devo fare è integrarlo in un pool di script dinamicamente ridimensionabile ... – nEJC

+0

Conosci qualche equivalente per Jython 2.5? – Laurent

+0

@Laurent - no, non ho guardato quest'area da quando ho postato questa risposta – McDowell

1

PythonInterpreter è costoso, questo codice ne utilizzerà 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

Interessante, ma per quanto ne so PythonInterpreter non è thread-safe, quindi probabilmente non è una buona idea (almeno per me) .. dovrò fare qualche test però ... – nEJC

+0

Sì, PythonInterpreter non è thread-safe, questo è esattamente il motivo per cui l'ho fatto in questo modo. pi.eval ("action.execute") fornisce solo un'istanza del metodo come un oggetto java, non lo esegue. – Ron