在Twisted中使用我自己的主循环

6 投票
2 回答
3631 浏览
提问于 2025-04-16 01:39

我有一个现成的程序,它有自己的主循环,并根据收到的输入进行计算——为了简单起见,我们假设输入来自用户。我现在想把计算过程从本地转移到远程进行,所以我决定在Twisted中实现远程过程调用(RPC)。

理想情况下,我只想修改我的一个函数,比如说 doComputation(),让它调用Twisted来执行RPC,获取结果后再返回。程序的其他部分应该保持不变。但是,我该怎么做呢?当我调用 reactor.run() 时,Twisted会接管主循环。我还了解到,Twisted中并没有真正的线程,所有任务都是按顺序运行的,所以我似乎不能仅仅创建一个循环调用(LoopingCall)来以这种方式运行我的主循环。

2 个回答

1

看起来这里正确且非常简单的答案是使用一个循环调用(LoopingCall):

http://www.saltycrane.com/blog/2008/10/running-functions-periodically-using-twisteds-loopingcall/

from datetime import datetime
from twisted.internet.task import LoopingCall
from twisted.internet import reactor

def doComputation():
    print "Custom fn run at", datetime.now()

lc = LoopingCall(doComputation)
lc.start(0.1)  # run your own loop 10 times a second

# put your other twisted here

reactor.run()
8

你有几种不同的选择,这取决于你现有程序的主循环是什么样的。

如果你的主循环是来自一个图形用户界面(GUI)库,Twisted可能已经支持它。在这种情况下,你可以直接使用它。

你也可以自己写一个反应器(reactor)。虽然这方面的文档不多,但你可以看看qtreactor是如何在Twisted外部实现反应器插件的

你还可以使用threadedselectreactor来写一个简单的反应器。这方面的文档也不多,但wxpython反应器就是用它实现的。个人来说,我不推荐这种方法,因为测试起来比较困难,可能会导致一些混乱的竞争条件,但它的好处是可以让你几乎使用Twisted的所有默认网络代码,只需加一层薄薄的封装。

如果你真的确定不想让你的doComputation是异步的,并且希望程序在等待Twisted回应时阻塞,可以按照以下步骤操作:

  • 在主循环启动之前,在另一个线程中启动Twisted,像这样:twistedThread = Thread(target=reactor.run); twistedThread.start()
  • 在你自己主循环的线程中实例化一个对象来进行RPC通信(比如叫RPCDoer),这样你就可以引用它。确保用reactor.callFromThread来启动它的Twisted逻辑,这样你就不需要为所有的Twisted API调用加上封装。
  • 实现RPCDoer.doRPC,让它返回一个Deferred,只使用Twisted的API调用(也就是说,不要调用你现有的应用程序代码,这样就不需要担心你的应用对象的线程安全;把doRPC所需的信息作为参数传递给它)。
  • 现在你可以这样实现doComputation

    def doComputation(self):
        rpcResult = blockingCallFromThread(reactor, self.myRPCDoer.doRPC)
        return self.computeSomethingFrom(rpcResult)
    
  • 记得在主循环的关闭程序中调用reactor.callFromThread(reactor.stop); twistedThread.join(),否则你可能会在退出时看到一些混乱的追踪信息或日志消息。

最后,有一个你真的应该考虑的选项,特别是从长远来看:放弃你现有的主循环,想办法直接使用Twisted的主循环。根据我的经验,这对10个问这个问题的人中有9个来说是正确的答案。我并不是说这总是是最佳选择——有很多情况下你确实需要保留自己的主循环,或者去掉现有循环的工作量太大。但维护自己的循环也是一项工作。请记住,Twisted的循环经过了数百万用户的广泛测试,并在各种环境中使用。如果你的循环也非常成熟,那可能没什么大问题,但如果你正在编写一个小的、新的程序,可靠性上的差异可能会很显著。

撰写回答