在Twisted应用中使用延迟对象

4 投票
1 回答
1918 浏览
提问于 2025-04-17 04:55

我感觉在写Twisted应用程序(.tac文件)时,有些东西我没搞明白。在.py脚本中使用延迟对象很简单,只需要在最后调用reactor.run(),但是我在任何Twisted应用示例代码中都没看到使用reactor.run()

能不能解释一下:

  1. 为什么在Twisted应用中不调用reactor.run()(或者说这是个错误的结论)
  2. 我如何在Twisted应用中使用延迟对象,也许不需要调用reactor.run()
  3. 还有编写Twisted脚本和应用程序的一般区别。

1 个回答

12

1. 为什么在示例的 .tac 文件中不调用 reactor.run()

.tac 文件是为了通过 "twistd" 命令行工具来加载的,这个工具会为你运行反应器。

运行反应器的操作只需要做一次,由你程序的主入口代码来完成。大多数 Twisted 代码实际上是某种插件,旨在更大的系统中运行。

具体来说,.tac 文件并不是为了作为独立的 Python 程序来运行的:它们的任务是生成一个 Application 对象(里面附带一堆 Service 对象),这些对象会在反应器运行时启动。重要的是,tac 文件本身不应该做太多工作,因为(例如)某些 Service 实现可能需要分开运行特权和非特权的代码,这个过程是比较复杂的;如果在 .tac 文件中执行工作,可能会因为用户权限不对而导致执行错误。

2. 如何在 Twisted 应用中使用 Deferred,而不调用 reactor.run()

Deferred 只是一个管理回调链的机制。你不需要调用 reactor.run(),甚至不需要反应器,也可以使用它们。例如:

>>> from twisted.internet.defer import Deferred
>>> d = Deferred()
>>> def hello(result):
...     print "'d' was fired:", result
...     return result + 3
... 
>>> d.addCallback(hello)
<Deferred at ...>
>>> print d
<Deferred at ...>
>>> d.callback(7)
'd' was fired: 7
>>> print d
<Deferred at ... current result: 10>

不过,许多返回 Deferred 的 API 需要反应器来完成一些工作,以便最终调用 .callback()。例如,如果你这样做...

>>> from twisted.internet.task import deferLater
>>> from twisted.internet import reactor
>>> deferLater(reactor, 1.0, lambda: 20).addCallback(hello)
<Deferred at ...>
>>>

...你将永远在等待,除非有人运行反应器。在那之前,什么都不会打印出来。

但是,如果反应器已经在运行——比如说,如果你在 python -m twisted.conch.stdio 中运行这个交互示例,而不是直接用 python,你会看到 Deferred 在一秒后被调用,因为那个交互提示已经在运行反应器。

3. Twisted 脚本和应用之间有什么区别?

这些其实并不是正式分开的术语。任何 Python 脚本都可以导入 Twisted 的代码并以任何方式使用它,所以很难说有什么特定的属性适用于“脚本”,除了它们都是计算机程序 :-)。

如果你说的 Twisted 应用是指 .tac 文件或插件,那么区别在于这类代码被分成了构建服务的部分(在你的 tac 文件或插件的顶层代码)和实际执行工作的部分(privilegedStartService/startService/stopService 的实现)。此外,在这个上下文中运行的代码(即由 twistd 驱动的代码)不需要自己运行反应器,因为反应器会由 twistd 自己设置和运行。因此,这类代码也必须小心避免导入 twisted.internet.reactor,因为 twistd 提供了使用不同反应器的能力(selectpollepollkqueue 等),如果在 twistd 有机会设置之前自己导入反应器,会破坏这个功能。

撰写回答