2010-07-21 6 views
6

Ho un programma esistente che ha il suo loop principale e calcola i calcoli in base all'input che riceve, diciamo dall'utente, per renderlo semplice. Ora voglio eseguire i calcoli in remoto anziché in locale e ho deciso di implementare gli RPC in Twisted.Usa il mio loop principale in twistato

Idealmente, voglio solo modificare una delle mie funzioni, ad esempio doComputation(), per effettuare una chiamata a twistato per eseguire l'RPC, ottenere i risultati e tornare. Il resto del programma dovrebbe rimanere lo stesso. Come posso realizzare questo, però? Twisted dirotta il loop principale quando chiamo reactor.run(). Ho anche letto che in realtà non hai thread in twist, che tutte le attività sono eseguite in sequenza, quindi sembra che non riesca solo a creare un LoopingCall e ad eseguire il mio ciclo principale in questo modo.

risposta

8

Avete un paio di opzioni diverse, a seconda del tipo di loop principale del vostro programma esistente.

Se è un mainloop da una libreria della GUI, Twisted may already have support for it. In tal caso, puoi semplicemente andare avanti e usarlo.

Si potrebbe anche scrivere il proprio reattore. Non c'è molta documentazione per questo, ma you can look at the way that qtreactor implementa esternamente un plugin per reattore a Twisted.

È anche possibile scrivere un reattore minimo utilizzando threadedselectreactor. Anche la documentazione per questo è scarsa, ma lo si utilizza con lo the wxpython reactor. Personalmente non consiglierei questo approccio in quanto è difficile da testare e potrebbe generare condizioni di gara confuse, ma ha il vantaggio di farvi sfruttare quasi tutto il codice di rete di Twisted con solo un sottile strato di wrapping.

Se siete veramente sicuri che non si desidera che il doComputation di essere asincrona, e si desidera che il programma per bloccare durante l'attesa per Twisted rispondere, effettuare le seguenti operazioni:

  • inizio ritorto in un altro thread prima che il tuo ciclo principale si avvii, con qualcosa come twistedThread = Thread(target=reactor.run); twistedThread.start()
  • istanzia un oggetto per fare la tua comunicazione RPC (diciamo, RPCDoer) nel thread del tuo ciclo principale, in modo da avere un riferimento ad esso. Assicurati di dare il via alla logica Twisted con reactor.callFromThread in modo da non dover includere tutte le sue chiamate API Twisted.
  • Implementare RPCDoer.doRPC per restituire un Deferred, utilizzando solo le chiamate Twisted API (ovvero non chiamare nel codice dell'applicazione esistente, quindi non è necessario preoccuparsi della sicurezza del thread per gli oggetti dell'applicazione, passare doRPC tutte le informazioni che esso bisogni come argomenti).
  • È ora possibile implementare doComputation così:

    def doComputation(self): 
        rpcResult = blockingCallFromThread(reactor, self.myRPCDoer.doRPC) 
        return self.computeSomethingFrom(rpcResult) 
    
  • ricordarsi di chiamare reactor.callFromThread(reactor.stop); twistedThread.join() dalla procedura di arresto il vostro principale del circuito, altrimenti si rischia di vedere alcuni traceback confuse o registrare i messaggi in uscita.

Infine, un'opzione da prendere in considerazione, soprattutto a lungo termine: scaricare il loop principale esistente e individuare un modo per utilizzare semplicemente Twisted. Nella mia esperienza questa è la risposta giusta per 9 su 10 di domande come questa. Non sto dicendo che questo è sempre la strada da percorrere - ci sono molti casi in cui hai davvero bisogno di mantenere il tuo ciclo principale, o dove è solo troppo sforzo per sbarazzarsi del ciclo esistente.Ma mantenere il tuo loop è anche un lavoro. Tieni presente che il loop Twisted è stato ampiamente testato da milioni di utenti e utilizzato in una grande varietà di ambienti. Se il tuo ciclo è anche estremamente maturo, potrebbe non essere un grosso problema, ma se stai scrivendo un piccolo, nuovo programma, la differenza di affidabilità potrebbe essere significativa.

+2

grazie per la grande risposta! Sto appoggiando il secondo al penultimo approccio, ma penso di sostituire il mio ciclo principale. ora, c'è un vantaggio nell'esecuzione ritorta in un thread separato rispetto a eseguirlo come thread principale e chiamando il mio ciclo principale con 'reactor.callInThread()'? – Claudiu

+0

blockingcallfromtrhead è la strada da percorrere – Claudiu

0

Sembra che la risposta corretta e molto semplice: ecco un LoopingCall:

http://www.saltycrane.com/blog/2008/10/running-functions-periodically-using-twisteds-loopingcall/

from datetime import datetime 
from twisted.internet.task import LoopingCall 
from twisted.internet import reactor 

def doComputation(): 
    print "Custom fn run at", datetime.now() 

lc = LoopingCall(doComputation) 
lc.start(0.1) # run your own loop 10 times a second 

# put your other twisted here 

reactor.run()