扭曲的延迟/回调与异步执行

7 投票
4 回答
11970 浏览
提问于 2025-04-16 09:06

我正在尝试弄清楚如何使用Twisted让我的代码更具异步性。

  • 一个函数返回了一个“延迟对象”(deferred object)。
  • 然后我添加了一系列的回调函数。
  • 第一个回调会在这个延迟对象通过deferred_obj.callback提供结果后被调用。
  • 接着,在这些回调的链条中,第一个回调会处理数据并调用第二个回调。
  • 依此类推。

不过,链式回调并不算异步,因为它们是串联在一起的,事件循环会一个接一个地执行这些回调,直到没有更多的回调可执行,对吧?

但是,如果我有一个延迟对象,并且我把d.addCallback(deferred_obj.callback)作为它的回调添加进去,那么这就会被视为异步,因为这个延迟对象在等待数据,而传递数据的方法也在等待数据。不过,一旦我调用了d.callback,这个对象就会处理数据,然后调用deferred_obj.callback。由于这个对象是延迟的,所以和链式回调不同,它会异步执行,对吧?

假设我的所有代码都是非阻塞的,这意味着链式回调不是异步的,而链式延迟是异步的,对吧?

4 个回答

0

使用延迟并不会让你的代码变成异步的。

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)把它分成几个短的部分来执行。

2

可以这么说,但这种事件处理方式并没有并发的概念。在代码回到事件循环之前,不会调用新的回调函数。所以这些回调函数是一个接一个地执行的,属于同步的。只有在事件循环中才算是异步的。

这是这种编程方式的一个注意事项,处理函数必须快速执行,并尽快返回到事件循环中。处理函数里不应该做任何耗时的任务。

9

回调函数默认是同步的,也就是说它们会一个接一个地执行。不过,正如Twisted文档所提到的:

如果你需要一个Deferred(延迟对象)等待另一个Deferred,只需要在添加回调的方法中返回一个Deferred。

这样你就可以在回调链中进行一些异步处理。我们来看看怎么做:

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

撰写回答