【问题标题】:Python Twisted Deferred : clarification neededPython Twisted Deferred:需要澄清
【发布时间】:2012-01-14 23:57:15
【问题描述】:

我希望对处理“第一个”deferreds 的最佳方法进行一些澄清,即不仅向返回延迟的现有 Twisted 方法添加回调和 errbacks,而是最好的方法创建那些原始延迟。

作为一个具体的例子,这里有两种相同方法的变体: 它只是计算一些相当大的文本文件中的行数,并用作延迟链的起点

方法一: 这个感觉不太好,因为 deferred 是由 reactor.callLater 方法直接触发的。

def get_line_count(self):
    deferred = defer.Deferred()

    def count_lines(result):
        try:
            print_file = file(self.print_file_path, "r")
            self.line_count = sum(1 for line in print_file)
            print_file.close()
            return self.line_count
        except Exception as inst:
            raise InvalidFile()

     deferred.addCallback(count_lines)
     reactor.callLater(1, deferred.callback, None)
     return deferred

方法二: 稍微好一点,因为延迟实际上是在结果可用时触发

def get_line_count(self):
    deferred = defer.Deferred()

    def count_lines():
        try:
            print_file = file(self.print_file_path, "r")
            self.line_count = sum(1 for line in print_file)
            print_file.close()
            deferred.callback(self.line_count)
        except Exception as inst:
            deferred.errback(InvalidFile())

    reactor.callLater(1, count_lines)
    return deferred

注意:您还可以指出,这两种方法实际上都是同步的,并且可能是阻塞方法(我也许可以使用“MaybeDeferred”?)。 但是,这实际上是我感到困惑的方面之一。

  1. 对于方法 2,如果 count_lines 方法非常慢(计算一些大文件中的行数等),它是否可能会“阻塞”整个扭曲的应用程序? 我阅读了很多关于回调和 errbacks 以及反应器如何一起工作的文档(回调需要快速执行,或者自己返回延迟等),但在这种情况下,我只是没有看到并且真的很感激一些指针/示例等

  2. 是否有一些文章/明确解释处理创建这些“第一”延迟的最佳方法?我已经通读了these excellent articles,他们对一些基本的理解有很大帮助,但我仍然觉得我缺少一部分。

  3. 对于阻塞代码,这是 DeferToThreadreactor.spawnprocess 的典型情况吗? 我阅读了很多问题,例如 this onethis article,但我仍然不能 100% 确定如何处理潜在的阻塞代码,主要是在处理文件 i/o 时

对不起,如果这看起来太基本了,但我真的想更彻底地掌握使用 Twisted 的窍门。 (对于所有更多面向网络的方面来说,它都是一个非常强大的工具)。 感谢您的宝贵时间!

【问题讨论】:

    标签: python twisted deferred


    【解决方案1】:

    是的,你没看错:你需要线程或单独的进程来避免阻塞 Twisted 事件循环。使用 Deferreds 不会神奇地使您的代码非阻塞。对于您的问题:

    1. 是的,如果count_lines 非常慢,你会阻塞事件循环。将它推迟到一个线程可以解决这个问题。

    2. 我使用 Twisteds documentation 来了解 Deferreds 的工作原理,但我想您已经经历过。 database support 上的文章是信息,因为它清楚地表明该库是使用线程构建的。这就是您弥合同步与异步差距的方式。

    3. 如果呼叫确实被阻塞,那么您需要DeferToThread。 Python 本身是一种单线程,这意味着一次只有一个线程可以执行 Python 字节码。但是,如果您创建的线程无论如何都会阻塞 I/O,那么这个模型可以正常工作:线程将释放全局解释器锁,让其他 Python 线程运行,包括带有 Twisted 事件循环的主线程。

      您也可以在代码中使用非阻塞 I/O。例如,这可以通过select 模块完成。在这种情况下,您不需要单独的线程。 Twisted 在内部使用这种技术,如果你做普通的网络 I/O,你不必考虑这个。但是,如果您正在做一些奇特的事情,那么最好了解事物是如何构建的,这样您就可以做同样的事情。

    我希望这能让事情更清楚一点!

    【讨论】:

    • 非常感谢马丁的快速而彻底的回复! 1. 和 2. 好的,线程/线程池方法确实在这种情况下有意义。
    • 3.由于 GIL,我倾向于对 Python 中的线程保持警惕,但通常它应该能够处理大约同时运行 count_lines 方法的 3-4 个线程,而不会弄乱 Twisted 事件循环(该方法用于小型“任务”,然后通过串行将数据从文件发送到设备)
    • 感谢有关选择模块的信息,它似乎与 Twisted 中不同类型的反应器“相关”。我会更详细地研究它!对于实际使用,不幸的是它只支持 Windows 平台上的套接字(因此在 Windows 上使用扭曲串行支持时我遇到了相当奇怪的限制)。
    • 一个子问题:如果count_lines(或任何其他方法)不慢/阻塞怎么办? 方法 2 是否是编写返回延迟并在“计算”完成时触发延迟的方法的“更好”方法?
    • 如果函数不慢,那么我就直接调用它——当我使用 Twisted 时,我从未在某些测试代码之外使用过 callLater
    猜你喜欢
    • 2011-11-09
    • 2012-07-18
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2017-11-26
    • 2012-06-02
    • 2017-01-18
    • 2016-06-30
    相关资源
    最近更新 更多