витая отложенная / обратные вызовы и асинхронное выполнение - PullRequest
6 голосов
/ 28 декабря 2010

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

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

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

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

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

Ответы [ 4 ]

8 голосов
/ 29 декабря 2010

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

Если вам нужно, чтобы один Deferred ожидал другого, все, что вам нужно сделать, это вернуть Deferred из метода, добавленного в 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()
2 голосов
/ 28 декабря 2010

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

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

0 голосов
/ 06 апреля 2016

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

https://github.com/iogf/untwisted

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

0 голосов
/ 28 декабря 2010

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

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()

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

...