Извращенные отложенные/обратные вызовы и асинхронное исполнение

StackOverflow https://stackoverflow.com/questions/4541853

Вопрос

Я пытаюсь выяснить, как я могу сделать свой код более асинхронным, используя Twisted.

  • Функция возвращает отложенный объект
  • Затем я добавляю список обратных вызовов
  • Первый обратный вызов будет вызван после того, как отложенная функция обеспечит некоторый результат через deferred_obj.callback
  • Затем, в цепочке обратных вызовов, первый обратный вызов сделает что -то с данными и позвонит второй обратный вызов
  • и так далее.

Однако обратные вызовы при цепях не будут считаться асинхронными, потому что они прикованные, и цикл событий будет продолжать стрелять в каждого из них одновременно, пока не будет больше, верно?

Однако, если у меня есть отложенный объект, и я прикрепляю его в качестве его обратного вызова d.addCallback(deferred_obj.callback) Тогда это будет считаться асинхронным, потому что deferred_obj ждет данных, а затем метод, который будет передавать данные, также ждет данных, однако, как только я d.callback 'd' обрабатывает данные, он вызовет deferred_obj. Обратный вызов, однако, поскольку этот объект отложен, в отличие от случая обратных прицепок, он будет выполнять асинхронно ... правильно?

Предполагая, что весь мой код не блокирует, это означает, что прицельные обратные вызовы не являются асинхронными, в то время как отложенные в цепи, верны?

Это было полезно?

Решение

Обратные вызовы (по умолчанию) синхронны. Однако как Извращенный док указывает на то:

Если вам нужно, чтобы один отложено ждать с другой, все, что вам нужно сделать, это вернуть отложенную из метода, добавленного в AddCallbacks.

Таким образом, вы можете использовать это, чтобы сделать некоторую асинхронную обработку в вашей цепочке обратного вызова. Давайте сделаем это:

from twisted.internet import reactor, defer

def callback_func_2(result, previous_data):
    # here we pass the result of the deferred down the callback chain
    # (done synchronously)
    print "calling function 1 on result:%s with previous result:%s" % (result, previous_data)
    return result

def callback_func(result):
    #let's do some asynchronous stuff in this callback
    # simple trick here is to return a deferred from a callback 
    # instead of the result itself.
    # 
    # so we can do asynchronous stuff here, 
    # like firing something 1 second later and have 
    # another method processing the result
    print "calling function 1 on result:%s" % result
    d = defer.Deferred()
    reactor.callLater(1, d.callback, "second callback")
    d.addCallback(callback_func_2, result)
    return d

def do():
    d = defer.Deferred()
    reactor.callLater(1, d.callback, "first callback")
    d.addCallback(callback_func)
    return d

do()
reactor.run()

Другие советы

Вроде, но в этом типе обработки событий нет параллелизма. Никакого нового обратного вызова не будет вызвано до тех пор, пока код вернется в цикл событий. Таким образом, цепочка обратных вызовов синхронна. Это только асинхронно в цикле событий.

Это одно предостережение этого типа программирования, обработчики наиболее быстро выполняются и возвращаются к циклу событий как можно скорее. Это не должно выполнять трудоемкую задачу в обработчике.

Использование отложенного не делает ваш код асинхронным.

import time
from twisted.internet import defer
from twisted.internet import reactor

def blocking(duration, deferred):
    print "start blocking"
    time.sleep(duration)
    print "finished blocking"
    deferred.callback(True)

def other_task():
    print "working..."
    reactor.callLater(1, other_task)

def finish(result):
    print "stopping reactor in 2sec"
    reactor.callLater(2, reactor.stop)

def failed(reason):
    print reason
    print "stopping reactor in 2sec"
    reactor.callLater(2, reactor.stop)

def main():
    d = defer.Deferred()
    d.addCallbacks(finish, failed)
    reactor.callLater(0, blocking, 5, d)

if __name__ == "__main__":
    reactor.callLater(0, other_task)
    main()
    reactor.run()

Если у вас есть давний синхронный код, вы можете отложить или разбить его на короткие итерации, используя кооператор (Twisted.internet.task)

Если вы хотите сделать свой код более асинхронным с чистым подходом, ознакомьтесь с этой структурой:

https://github.com/iogf/untwisted

Он имеет аккуратный код с четкой документацией. Подход к борьбе с асинхронными моделями прост.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top