2010-09-10 16 views
8

Quelle est la différence entre defer.execute() et threads.deferToThread() dans torsadée? Les deux prennent les mêmes arguments - une fonction, et les paramètres pour l'appeler avec - et retournent un différé qui sera déclenché avec le résultat de l'appel de la fonction. La version threads indique explicitement qu'elle sera exécutée dans un thread. Toutefois, si la version defer ne fonctionne pas, alors quel serait le point de l'appeler? Le code qui fonctionne dans le réacteur ne devrait jamais bloquer, donc toute fonction qu'il appelle ne devrait pas bloquer. À ce stade, vous pouvez simplement faire defer.succeed(f(*args, **kwargs)) au lieu de defer.execute(f, args, kwargs) avec les mêmes résultats.tordu: différence entre `defer.execute` et` threads.deferToThread`

Répondre

9

defer.execute n'exécute en effet la fonction d'une manière de blocage, dans le même fil et vous avez raison dans ce defer.execute(f, args, kwargs) fait la même chose que defer.succeed(f(*args, **kwargs))sauf quedefer.execute renverra un rappel qui a eu le feu errback si la fonction f déclenche une exception. Pendant ce temps, dans votre exemple defer.succeed, si la fonction lançait une exception, elle se propagerait vers l'extérieur, ce qui n'est peut-être pas souhaitable.

Pour faciliter la compréhension, je vais juste coller la source de defer.execute ici:

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) 

En d'autres termes, defer.execute est juste un raccourci pour prendre le résultat d'une fonction de blocage en différé que vous pouvez puis ajoutez les callbacks/errbacks à. Les rappels seront déclenchés avec une sémantique de chaînage normale. Cela semble un peu fou, mais les différés peuvent «tirer» avant que vous ajoutiez des rappels et les rappels seront encore appelés.


Pour répondre à votre question, pourquoi est-ce utile? Eh bien, defer.execute est utile à la fois pour tester/se moquer ainsi que d'intégrer simplement une API asynchrone avec du code synchrone.

Aussi utile est defer.maybeDeferred qui appelle la fonction et si la fonction renvoie déjà un différé renvoie simplement, sinon des fonctions similaires à defer.execute. Ceci est utile lorsque vous écrivez une API qui attend un appelable qui, lorsqu'il est appelé, vous donne un différé, et vous voulez aussi pouvoir accepter les fonctions de blocage normales. Par exemple, supposons que vous ayez une application qui a récupéré des pages et fait des choses avec. Et, pour une raison quelconque, vous avez dû exécuter cette opération de manière synchrone pour un cas d'utilisation spécifique, comme dans un script crontab à une seule impulsion, ou en réponse à une requête dans une application WSGI, tout en conservant la même base de code. Si votre code ressemblait à ceci, il pourrait se faire:

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 

Pour exécuter ce dans un contexte synchrone, sans le réacteur, vous pouvez simplement passer une fonction de lecture alternative, comme ceci:

from urllib2 import urlopen 

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

+ 1 pour expliquer defer.maybeDeferred –