twisted:检查一个延迟对象是否已经被调用
我想实现的目标是这样的:我需要向服务器发起一个远程请求来获取信息,并且希望程序在获取到信息之前能够暂停等待。我创建了一个函数,它会返回一个“延迟对象”(Deferred),这样当服务器回复时,这个延迟对象就会被调用。然后我有一个函数是从一个线程中调用的,代码是 threads.blockingCallFromThread(reactor, deferredfunc, args)
。
如果出现问题,比如服务器崩溃了,那么这个调用就会一直处于等待状态,永远不会解除阻塞。我希望在这种情况下,延迟对象能够抛出一个异常。
我部分成功了。我有一个延迟对象 onConnectionLost
,它会在连接丢失时被触发。我修改了我的阻塞调用函数,变成了:
deferred = deferredfunc(args)
self.onConnectionLost.addCallback(lambda _: deferred.errback(
failure.Failure(Exception("connection lost while getting run"))))
result = threads.blockingCallFromThread(
reactor, lambda _: deferred, None)
return result
这样做效果很好。如果服务器崩溃,连接就会丢失,错误回调(errback)会被触发。然而,如果服务器没有崩溃,一切正常关闭,onConnectionLost
仍然会被触发,而这里的匿名回调又试图触发错误回调,导致出现 AlreadyCalled
的异常。
有没有什么好的方法可以检查一个延迟对象是否已经被触发过?我想避免把它放在 try/except
块中,但如果这是唯一的办法,我也可以这样做。
1 个回答
有一些方法可以做到,但其实你不应该这么做。触发Deferred
的代码应该记录它是否已经被触发过。实际上,当你触发Deferred
时,你应该不再关注它,这样它才能被正确地垃圾回收;这样你就不用担心会重复调用它,因为你已经不再持有对它的引用了。
另外,看起来你是在调用blockingCallFromThread
的同一个线程中调用deferredfunc
。这样做是不对的;返回Deferreds
的函数很可能在调用反应器的API,而这些API是不安全的,也就是说它们不能在多个线程中同时使用。实际上,Deferred
本身也是不安全的。这就是为什么我们用blocking
Call
FromThread
,而不是blockOnThisDeferredFromThread
。你应该使用blockingCallFromThread(reactor, deferredfunc, args)
。
如果你真的想要一种“如果已经调用则执行错误回调,否则什么都不做”的行为,你可能需要取消这个Deferred。