2010-09-10 19 views
8

Qual è la differenza tra defer.execute() e threads.deferToThread() in twisted? Entrambi accettano gli stessi argomenti - una funzione e i parametri con cui chiamarlo - e restituiscono un rinvio che verrà generato con il risultato di chiamare la funzione.twistato: differenza tra `defer.execute` e` threads.deferToThread`

La versione threads afferma esplicitamente che verrà eseguita in una discussione. Tuttavia, se la versione defer no, allora quale sarebbe il punto di chiamarla? Il codice che viene eseguito nel reattore non dovrebbe mai bloccare, quindi qualsiasi funzione chiamata non dovrebbe bloccarsi. A quel punto, potresti semplicemente fare defer.succeed(f(*args, **kwargs)) anziché lo defer.execute(f, args, kwargs) con gli stessi risultati.

risposta

9

defer.execute effettivamente eseguire la funzione in modo blocco, nello stesso thread e si è corretta in quanto defer.execute(f, args, kwargs) fa lo stesso come defer.succeed(f(*args, **kwargs))tranne che defer.execute restituirà un callback che ha avuto l'errback licenziato se la funzione f genera un'eccezione. Nel frattempo, nell'esempio defer.succession, se la funzione ha generato un'eccezione, si propagherebbe verso l'esterno, cosa che potrebbe non essere desiderata.

Per una maggiore comprensione, mi limiterò a incollo la fonte di defer.execute qui:

def execute(callable, *args, **kw): 
    """Create a deferred from a callable and arguments. 

    Call the given function with the given arguments. Return a deferred which 
    has been fired with its callback as the result of that invocation or its 
    errback with a Failure for the exception thrown. 
    """ 
    try: 
     result = callable(*args, **kw) 
    except: 
     return fail() 
    else: 
     return succeed(result) 

In altre parole, defer.execute è solo una scorciatoia per prendere il risultato di una funzione di blocco come differito che è possibile quindi aggiungere callback/errback a. I callback verranno attivati ​​con la semantica di concatenazione normale. Sembra un po 'pazzo, ma i Deferred possono "sparare" prima di aggiungere i callback e i callback saranno ancora chiamati.


Quindi, per rispondere alla tua domanda, perché è questo utile? Bene, defer.execute è utile sia per il testing/mocking che per la semplice integrazione di un'async asincrono con codice sincrono.

Anche utile è defer.maybeDeferred che chiama la funzione e quindi se la funzione restituisce già un rinvio, semplicemente lo restituisce, altrimenti funziona come defer.execute. Questo è utile quando scrivi un'API che si aspetta un callable che quando viene chiamato ti dà un rinvio e vuoi essere in grado di accettare anche le normali funzioni di blocco.

Ad esempio, supponiamo che tu abbia un'applicazione che recupera le pagine e ne fa le cose. E, per qualche ragione, è necessario eseguire questo in modo sincrono per un caso d'uso specifico, come in uno script crontab single-shot, o in risposta a una richiesta in un'applicazione WSGI, mantenendo comunque la stessa base di codice. Se il codice si presentava così, potrebbe essere fatto:

from twisted.internet import defer 
from twisted.web.client import getPage 

def process_feed(url, getter=getPage): 
    d = defer.maybeDeferred(getter, url) 
    d.addCallback(_process_feed) 

def _process_feed(result): 
    pass # do something with result here 

Per eseguire questo in un contesto sincrono, senza il reattore, si può solo passare una funzione getter alternativa, in questo modo:

from urllib2 import urlopen 

def synchronous_getter(url): 
    resp = urlopen(url) 
    result = resp.read() 
    resp.close() 
    return result 
+0

+ 1 per spiegare defer.maybeDeferred –