витая: разница между `defer.execute` и` threads.deferToThread` - PullRequest
8 голосов
/ 10 сентября 2010

В чем разница между defer.execute() и threads.deferToThread() в витых? Оба принимают одинаковые аргументы - функцию и параметры для вызова - и возвращают deferred, который будет вызван результатом вызова функции.

Версия threads явно заявляет, что она будет запущена в потоке. Однако, если версия defer этого не делает, какой смысл ее называть? Код, работающий в реакторе, никогда не должен блокировать, поэтому любая функция, которую он вызывает, не должна блокироваться. В этот момент вы можете просто сделать defer.succeed(f(*args, **kwargs)) вместо defer.execute(f, args, kwargs) с такими же результатами.

1 Ответ

9 голосов
/ 10 сентября 2010

defer.execute действительно выполняет функцию блокирующим образом, в том же потоке, и вы правы в том, что defer.execute(f, args, kwargs) делает то же самое, что и defer.succeed(f(*args, **kwargs)) , за исключением , что defer.execute возвращаетобратный вызов, который вызвал ошибку, если функция f выдает исключение.Между тем, в вашем примере defer.succeed, если функция выдает исключение, она будет распространяться наружу, что может быть нежелательно.

Для простоты понимания я просто вставлю сюда источник defer.execute:

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)

Другими словами, defer.execute - это просто ярлык для получения результата функции блокировки в качестве отложенного, к которому можно затем добавить обратные вызовы / ошибки.Обратные вызовы будут выполняться с нормальной семантикой цепочки.Это кажется немного сумасшедшим, но Deferreds может «выстрелить» до того, как вы добавите обратные вызовы, и обратные вызовы все равно будут вызываться.


Итак, чтобы ответить на ваш вопрос, почему это полезно ?Что ж, defer.execute полезен как для тестирования / проверки, так и для простой интеграции асинхронного API с синхронным кодом.

Также полезным является defer.maybeDeferred, который вызывает функцию, а затем, если функция уже возвращает deferred, просто возвращает ее, в противном случае функции аналогичны defer.execute.Это полезно, когда вы пишете API, который ожидает вызываемый объект, который при вызове дает вам отложенный, и вы хотите иметь возможность принимать и обычные функции блокировки.

Например, скажем, у вас есть приложение, котороевыбирал страницы и делал что-то с этим.И по какой-то причине вам нужно было запускать это синхронно для конкретного случая использования, например, в сценарии crontab с одним выстрелом или в ответ на запрос в приложении WSGI, но при этом сохраняйте ту же кодовую базу.Если ваш код выглядит следующим образом, это можно сделать:

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

Чтобы запустить это в синхронном контексте, без реактора, вы можете просто передать альтернативную функцию получения, например, так:

from urllib2 import urlopen

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