twisted:检查一个延迟对象是否已经被调用

2 投票
1 回答
2883 浏览
提问于 2025-04-16 01:51

我想实现的目标是这样的:我需要向服务器发起一个远程请求来获取信息,并且希望程序在获取到信息之前能够暂停等待。我创建了一个函数,它会返回一个“延迟对象”(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 个回答

5

有一些方法可以做到,但其实你不应该这么做。触发Deferred的代码应该记录它是否已经被触发过。实际上,当你触发Deferred时,你应该不再关注它,这样它才能被正确地垃圾回收;这样你就不用担心会重复调用它,因为你已经不再持有对它的引用了。

另外,看起来你是在调用blockingCallFromThread的同一个线程中调用deferredfunc。这样做是不对的;返回Deferreds的函数很可能在调用反应器的API,而这些API是不安全的,也就是说它们不能在多个线程中同时使用。实际上,Deferred本身也是不安全的。这就是为什么我们用blockingCallFromThread,而不是blockOnThisDeferredFromThread。你应该使用blockingCallFromThread(reactor, deferredfunc, args)

如果你真的想要一种“如果已经调用则执行错误回调,否则什么都不做”的行为,你可能需要取消这个Deferred。

撰写回答