2010-09-10 3 views
8

Was ist der Unterschied zwischen defer.execute() und threads.deferToThread() in twisted? Beide nehmen die gleichen Argumente an - eine Funktion und Parameter, mit denen sie aufgerufen werden - und geben ein verzögertes zurück, das mit dem Ergebnis des Aufrufs der Funktion ausgelöst wird.twisted: Unterschied zwischen `defer.execute` und` threads.deferToThread`

Die threads Version gibt explizit an, dass es in einem Thread ausgeführt wird. Jedoch, wenn die defer Version nicht, dann, was würde jemals der Punkt sein, es zu nennen? Code, der im Reaktor läuft, sollte niemals blockieren, daher muss jede Funktion, die er aufruft, nicht blockieren. An diesem Punkt könnten Sie einfach defer.succeed(f(*args, **kwargs)) anstelle von defer.execute(f, args, kwargs) mit den gleichen Ergebnissen tun.

Antwort

9

defer.execute ausführt tatsächlich die Funktion in einer blockierenden Art und Weise, in dem gleichen Thread und Sie sind richtig, dass defer.execute(f, args, kwargs) macht die gleichen wie defer.succeed(f(*args, **kwargs))außer dass defer.execute einen Rückruf zurück, der die errback hatte gefeuert, wenn die Funktion f löst eine Ausnahme aus. Wenn in Ihrem Beispiel defer.succeed die Funktion eine Ausnahme ausgelöst hat, würde sie sich nach außen ausbreiten, was nicht erwünscht sein könnte.

Zum leichteren Verständnis, ich werde nur die Quelle defer.execute hier einfügen:

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) 

Mit anderen Worten, ist defer.execute nur eine Verknüpfung einer Sperrfunktion des Ergebnis zu nehmen, wie eine Dose, die Sie verschoben Fügen Sie dann Callbacks/Errbacks hinzu. Die Callbacks werden mit normaler Verkettungssemantik abgefeuert. Es scheint ein bisschen verrückt, aber Verzögerter kann "feuern" bevor Sie Callbacks hinzufügen und die Callbacks werden immer noch aufgerufen.


So Ihre Frage zu beantworten, warum ist das nützlich? Nun, defer.execute ist sowohl zum Testen/Mocking als auch zum einfachen Integrieren einer asynchronen API mit synchronem Code nützlich.

Auch nützlich ist defer.maybeDeferred, die die Funktion aufruft und dann, wenn die Funktion bereits zurückgibt, verzögert einfach zurück, sonst funktioniert ähnlich wie defer.execute. Dies ist nützlich, wenn Sie eine API schreiben, die eine aufrufbare Funktion erwartet, die Ihnen beim Aufrufen eine Verzögerung gibt und Sie auch normale Blockierungsfunktionen akzeptieren möchten.

Zum Beispiel, sagen Sie, Sie hatten eine Anwendung, die Seiten abgerufen und Dinge damit gemacht hat. Und aus irgendeinem Grund mussten Sie dies synchron für einen bestimmten Anwendungsfall ausführen, z. B. in einem Single-Shot-Crontab-Skript oder als Reaktion auf eine Anfrage in einer WSGI-Anwendung, aber immer noch die gleiche Codebasis. Wenn Ihr Code so aussah, könnte es geschehen:

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 

dies in einem synchronen Kontext ausführen zu können, ohne den Reaktor, könnte man einfach eine alternative Getter-Funktion übergeben, etwa so:

from urllib2 import urlopen 

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

+ 1 zur Erläuterung defer.maebeDeferred –