假设在一个扭曲的Python程序中有一个同步函数,需要很长时间才能执行,在很多合理大小的工作中执行此操作。如果函数可以返回延迟,那么这将是一个明智的选择,但是函数恰好在某些同步代码中很深,所以让延迟继续是不可能的。
有没有办法让扭曲处理突出事件而不离开该功能?即我想要做的是
def my_func():
results = []
for item in a_lot_of_items():
results.append(do_computation(item))
reactor.process_outstanding_events()
return results
当然,这对代码强加了重入性要求,但是在Qt中仍有QCoreApplication.processEvents,有什么扭曲吗?
答案 0 :(得分:1)
一些基于事件循环的系统(基本上是您通过Qt' QCoreApplication.processEvents
API引用的解决方案)采用的解决方案是使主循环重入。在Twisted术语中,这意味着(不工作代码):
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()
注意此程序中有两个 reactor.run
调用。如果Twisted有一个重入事件循环,则第二次调用将再次开始旋转反应器,并且在遇到匹配的reactor.stop
调用之前不会返回。反应堆将处理它所知道的所有事件,而不仅仅是do_work
生成的事件,因此你会得到你想要的行为。
这需要一个重入事件循环,因为反应器循环已经调用了my_expensive_task_...
。反应器循环在调用堆栈上。然后,调用reactor.run
并且反应器循环现在再次位于调用堆栈 。因此通常的问题适用:事件循环不能在其帧中留下状态(否则在嵌套调用完成时它可能无效),在任何调用其他代码时,它不能使其实例状态不一致等。
Twisted没有重入事件循环。这是一个已被考虑过的功能,至少在过去被明确拒绝。支持此功能会为实现和应用程序带来大量额外的复杂性(如上所述)。如果事件循环是可重入的,那么很难避免要求所有应用程序代码都是可重入的。这否定了协同多任务处理方法的主要优点之一Twisted采用并发性(保证您的函数不会被重新输入)。
所以,当使用Twisted时,这个解决方案就出来了。
我不知道另一个允许您继续在reactor线程中运行此代码的解决方案。您提到有问题的代码嵌套在其他一些同步代码中。我想到的其他选择是:
答案 1 :(得分:0)
您可以使用deferToThread。
http://twistedmatrix.com/documents/13.2.0/core/howto/threading.html
该方法在单独的线程中运行您的计算,并返回在计算实际完成时调用的延迟。
答案 2 :(得分:0)
这应该做:
for item in items:
reactor.callLater(0, heavy_func, item)
reactor.callLater会让你回到事件循环中。
答案 3 :(得分:0)
问题是如果do_heavy_computation()
是阻塞的代码,那么执行将不会转到下一个函数。在这种情况下,请使用deferToThread
或blockingCallFromThread
进行大量计算。此外,如果您不关心计算结果,则可以使用callInThread
。看看documentation on threads