2010-09-10 23 views
8

Jaka jest różnica między defer.execute() i threads.deferToThread() w skręconej? Oba przyjmują te same argumenty - funkcję i parametry, aby wywołać ją - i zwracają odroczone, które zostaną uruchomione z wynikiem wywołania funkcji.skręcona: różnica między "defer.execute" i "threads.deferToThread"

Wersja threads wyraźnie stwierdza, że ​​zostanie uruchomiona w wątku. Jeśli jednak wersja defer nie, to jaki byłby sens jej wywoływania? Kod działający w reaktorze nie powinien nigdy blokować, więc jakakolwiek funkcja, którą wywołuje, nie będzie blokować. W tym momencie można po prostu zrobić defer.succeed(f(*args, **kwargs)) zamiast defer.execute(f, args, kwargs) z tymi samymi wynikami.

Odpowiedz

9

defer.execute rzeczywiście wykonać funkcję blokowania w sposób, w tym samym wątku i masz rację, że defer.execute(f, args, kwargs) działa tak samo jak defer.succeed(f(*args, **kwargs))wyjątkiem że defer.execute zwróci zwrotnego, który miał errback zwolniony jeśli funkcja f zgłasza wyjątek. W międzyczasie, w przykładzie odroczenia. Jeśli funkcja wyrzuci wyjątek, będzie propagować na zewnątrz, co może nie być pożądane.

Dla ułatwienia zrozumienia, po prostu wklej źródło defer.execute tutaj:

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) 

Innymi słowy, defer.execute to tylko skrót do podjęcia wynik funkcji blokowania jako odroczone, które można następnie dodaj callbacks/errbacks do. Callbacks będą uruchamiane z normalną semantyką łączenia. Wydaje się to trochę szalone, ale Deferreds może "strzelać", zanim dodasz callbacka, a callbacki będą nadal wywoływane.


Tak, aby odpowiedzieć na pytanie, dlaczego jest to przydatna? Cóż, defer.execute jest przydatny zarówno do testowania/szyderstwa, jak i prostej integracji api async z kodem synchronicznym.

Użyteczne jest także defer.maybeDeferred, które wywołuje funkcję, a następnie, jeśli funkcja już zwraca, odroczone po prostu ją zwraca, w przeciwnym razie działa podobnie do defer.execute. Jest to przydatne, gdy piszesz API, które oczekuje wywołania, które po wywołaniu daje odroczone, i chcesz móc również akceptować normalne funkcje blokowania.

Załóżmy na przykład, że masz aplikację, która pobrała strony i zrobiła z nią coś. I, z jakiegoś powodu, trzeba było uruchomić to w sposób synchroniczny dla konkretnego przypadku użycia, jak w pojedynczym skrypcie crontab lub w odpowiedzi na żądanie w aplikacji WSGI, ale nadal zachowywać ten sam kod. Jeśli kod wyglądał tak, można to zrobić:

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 

Aby uruchomić ten w synchronicznym kontekście bez reaktora, można po prostu przejść alternatywną funkcję pobierającą, tak:

from urllib2 import urlopen 

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

+ 1 dla wyjaśnienia defer.maybeDeferred –