twisted:处理同步代码中的传入事件

时间:2016-12-08 15:10:20

标签: python event-handling twisted

假设在一个扭曲的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,有什么扭曲吗?

4 个答案:

答案 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()是阻塞的代码,那么执行将不会转到下一个函数。在这种情况下,请使用deferToThreadblockingCallFromThread进行大量计算。此外,如果您不关心计算结果,则可以使用callInThread。看看documentation on threads