Python twisted:在同步代码中处理传入事件
假设在一个twisted支持的Python程序中有一个同步函数,它需要很长时间才能执行,这需要大量大小合理的工作。如果函数可以返回延迟,这将是一个不需要动脑筋的问题,但是函数恰好位于某些同步代码的深处,因此不可能让延迟继续 有没有一种方法可以让twisted在不离开该功能的情况下处理未完成的事件?也就是说,我想做的事情是Python twisted:在同步代码中处理传入事件,python,event-handling,twisted,Python,Event Handling,Twisted,假设在一个twisted支持的Python程序中有一个同步函数,它需要很长时间才能执行,这需要大量大小合理的工作。如果函数可以返回延迟,这将是一个不需要动脑筋的问题,但是函数恰好位于某些同步代码的深处,因此不可能让延迟继续 有没有一种方法可以让twisted在不离开该功能的情况下处理未完成的事件?也就是说,我想做的事情是 def my_func(): 结果=[] 对于一个或多个或多个项目()中的项目: 结果.追加(do_计算(项目)) 反应器。处理未完成的事件() 返回结果 当然,这对代码提出
def my_func():
结果=[]
对于一个或多个或多个项目()中的项目:
结果.追加(do_计算(项目))
反应器。处理未完成的事件()
返回结果
当然,这对代码提出了可重入性要求,但Qt中仍然有这样的要求,twisted中有什么吗?您可以使用deferToThread 该方法在单独的线程中运行您的计算,并返回一个延迟值,该值在计算实际完成时被回调。这应该可以:
for item in items:
reactor.callLater(0, heavy_func, item)
reactor.callLater应该将您带回事件循环 问题是如果
do\u heavy\u computation()
是阻塞的代码,那么执行将不会转到下一个函数。在这种情况下,使用deletothread
或blockingCallFromThread
进行繁重的计算。此外,如果您不关心计算结果,则可以使用callInThread
。看看一些基于事件循环的系统采用的解决方案(基本上就是您通过Qt的QCoreApplication.processEvents
API引用的解决方案)是使主循环重新进入。在扭曲的术语中,这意味着类似(非工作代码):
注意有两个反应器。在这个程序中运行调用。如果Twisted有一个可重入事件循环,那么第二次调用将再次开始旋转反应器,直到遇到匹配的反应器.stop
调用后才会返回。反应器将处理它所知道的所有事件,而不仅仅是由dou_work
生成的事件,因此你将拥有你想要的行为
这需要一个可重入事件循环,因为反应器循环已经在调用my\u-expensive\u-task…
。reactor循环位于调用堆栈上。然后,reactor.run
被调用,此时reactor循环再次位于调用堆栈上。因此,通常的问题是:事件循环不能在其框架中保留状态(否则,在嵌套调用完成时它可能无效),在调用其他代码时,它不能使其实例状态不一致,等等
Twisted没有可重入事件循环。这是一个已经被考虑过的特性,至少在过去被明确拒绝了。支持这些特性会给实现和应用程序带来大量额外的复杂性(如上所述)。如果事件循环是可重入的,那么就很难避免要求所有应用程序代码也是可重入安全的。这否定了Twisted对并发性的协作多任务方法的一个主要好处(即保证您的函数不会被重新输入)
因此,当使用Twisted时,此解决方案已过时
我不知道还有另一种解决方案允许您继续在reactor线程中运行此代码。您提到,所讨论的代码深入嵌套在其他一些同步代码中。想到的其他选项有:
- 使同步代码能够处理异步事务
- 首先计算出昂贵的部分,然后将结果传递给代码的其余部分
- 在另一个线程中运行所有这些代码,而不仅仅是计算代价高昂的部分
如果我可以将my_func
的结果作为延迟返回,那会有所帮助,但正如我所说的,它深深地隐藏在同步代码中,要返回延迟,我必须重写它,将延迟的结果一直带到事件循环中。我已经更新了问题,以反映单个迭代时间是可以的,相反,整个迭代时间太长了。好吧,我知道我可能会把人们和“重”部分混淆了。处理一个项目是好的,当有很多项目时,延迟真的开始生效。让我稍微修改一下这个问题。
def my_expensive_task_that_cannot_be_asynchronous():
@inlineCallbacks
def do_work(units):
for unit in units:
yield do_one_work_asynchronously(unit)
work = do_work(some_work_units())
work.addBoth(lambda ignored: reactor.stop())
reactor.run()
def main():
# Whatever your setup is...
# Then, hypothetical event source triggering your
# expensive function:
reactor.callLater(
30,
my_expensive_task_that_cannot_be_asynchronous,
)
reactor.run()