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
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
+ 1 pour expliquer defer.maybeDeferred –