Python Twisted Deferred:需要澄清

2 投票
1 回答
2583 浏览
提问于 2025-04-17 10:16

我想请教一下,处理“第一个”延迟对象的最佳方法是什么。也就是说,不仅仅是给现有的返回延迟对象的Twisted方法添加回调和错误回调,而是如何创建这些原始的延迟对象。

举个具体的例子,这里有两个相同方法的变体: 它只是计算一些比较大的文本文件中的行数,并作为一系列延迟对象的起点

方法 1: 这个方法感觉不太好,因为延迟对象是直接通过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

方法 2: 这个稍微好一点,因为延迟对象实际上是在结果可用时触发的。

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方法非常慢(比如计算一些超大文件的行数等),这会不会“阻塞”整个Twisted应用? 我读了很多关于回调、错误回调和反应器如何一起工作的文档(回调需要快速执行,或者自己返回延迟对象等),但在这种情况下,我就是看不明白,真的很希望能得到一些指点/示例等。

  2. 有没有一些文章/清晰的解释,讲述创建这些“第一个”延迟对象的最佳方法?我读过这些优秀的文章,它们在一些基本理解上帮助很大,但我仍然觉得缺少了一些东西。

  3. 对于阻塞代码,这是否是使用DeferToThreadreactor.spawnprocess的典型案例? 我读过很多类似于这个问题这篇文章,但我仍然不太确定如何处理可能会阻塞的代码,尤其是在处理文件输入输出时。

抱歉如果这些内容看起来太基础,但我真的想更深入地掌握使用Twisted的技巧。(它在网络相关的方面是一个非常强大的工具)。 谢谢你的时间!

1 个回答

2

没错,你理解得很对:为了避免阻塞Twisted的事件循环,你需要使用线程或独立的进程。使用Deferreds并不能神奇地让你的代码变得非阻塞。关于你的问题:

  1. 是的,如果count_lines这个函数非常慢,你会阻塞事件循环。把它放到一个线程里就能解决这个问题。

  2. 我曾经参考过Twisted的文档来了解Deferreds是怎么工作的,但我想你可能已经看过了。关于数据库支持的文章也很有用,因为它明确说明这个库是通过线程来构建的。这就是如何弥补同步和异步之间的差距。

  3. 如果调用确实会阻塞,那么你需要使用DeferToThread。Python本身有点像单线程,这意味着一次只能有一个线程执行Python的字节码。不过,如果你创建的线程本身就会在I/O上阻塞,那么这个模型是可以的:线程会释放全局解释器锁,让其他Python线程运行,包括主线程和Twisted的事件循环。

    也有可能你可以在代码中使用非阻塞的I/O。这可以通过select模块来实现。在这种情况下,你就不需要单独的线程了。Twisted在内部使用了这种技术,如果你进行正常的网络I/O,你不需要考虑这些。但如果你在做一些特殊的事情,了解这些构建方式是很有帮助的,这样你也可以做到同样的事情。

希望这些解释能让事情变得更清楚一些!

撰写回答