2010-09-10 15 views
8

¿Cuál es la diferencia entre defer.execute() y threads.deferToThread() en twisted? Ambos toman los mismos argumentos, una función y parámetros para invocarlos, y devuelven un diferido que se activará con el resultado de llamar a la función.twisted: diferencia entre `defer.execute` y` threads.deferToThread`

La versión threads establece explícitamente que se ejecutará en un hilo. Sin embargo, si la versión defer no lo hace, ¿cuál sería el motivo de llamarlo? El código que se ejecuta en el reactor nunca debe bloquearse, por lo que cualquier función que llame no debería bloquear. En ese punto, podría simplemente hacer defer.succeed(f(*args, **kwargs)) en lugar de defer.execute(f, args, kwargs) con los mismos resultados.

Respuesta

9

defer.execute en efecto, ejecutar la función de un modo de bloqueo, en el mismo hilo y que son correctas en que defer.execute(f, args, kwargs) hace lo mismo que defer.succeed(f(*args, **kwargs))excepto que defer.execute volverá una devolución de llamada que ha tenido el errback despedido si la función f arroja una excepción. Mientras tanto, en su ejemplo diferido, si la función arrojara una excepción, se propagaría hacia afuera, lo que puede no ser deseable.

Para facilitar la comprensión, sólo voy a pegar la fuente de defer.execute aquí:

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 otras palabras, defer.execute es sólo un atajo para tomar el resultado de una función de bloqueo como diferidos, que se puede luego agrega callbacks/errbacks a. Las devoluciones de llamada se dispararán con semántica de encadenamiento normal. Parece un poco loco, pero Deferreds puede 'disparar' antes de agregar callbacks y las devoluciones de llamada seguirán siendo llamadas.


Así que para responder a su pregunta, Por qué es útil? Bueno, defer.execute es útil tanto para probar/burlarse como para simplemente integrar una API asíncrona con código síncrono.

También es útil defer.maybeDeferred que llama a la función y luego si la función ya devuelve un aplazado simplemente lo devuelve, de lo contrario funciona de manera similar a defer.execute. Esto es útil para cuando se escribe una API que espera que un llamable que, cuando se llama, le otorgue un aplazado, y también se desee poder aceptar funciones de bloqueo normales.

Por ejemplo, supongamos que tiene una aplicación que ha buscado páginas e hizo cosas con ella. Y, por alguna razón, necesitó ejecutar esto de forma sincrónica para un caso de uso específico, como en una secuencia de comandos crontab de una sola toma, o en respuesta a una solicitud en una aplicación WSGI, pero conservando la misma base de código. Si el código se veía así, se podría hacer:

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 

Para ejecutar esto en un contexto sincrónico, sin el reactor, sólo podía pasar una función captador alternativo, así:

from urllib2 import urlopen 

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

+ 1 para explicar defer.maybeDeferred –