在Twisted应用中使用延迟对象
我感觉在写Twisted应用程序(.tac文件)时,有些东西我没搞明白。在.py脚本中使用延迟对象很简单,只需要在最后调用reactor.run()
,但是我在任何Twisted应用示例代码中都没看到使用reactor.run()
。
能不能解释一下:
- 为什么在Twisted应用中不调用
reactor.run()
(或者说这是个错误的结论) - 我如何在Twisted应用中使用延迟对象,也许不需要调用
reactor.run()
呢 - 还有编写Twisted脚本和应用程序的一般区别。
1 个回答
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
提供了使用不同反应器的能力(select
、poll
、epoll
、kqueue
等),如果在 twistd
有机会设置之前自己导入反应器,会破坏这个功能。