tornado.ioloop
— 主事件循环¶
用于非阻塞套接字的 I/O 事件循环。
在 Tornado 6.0 中,IOLoop
是 asyncio
事件循环的包装器,具有略微不同的接口。 IOLoop
接口现在主要用于向后兼容性;新代码通常应该直接使用 asyncio
事件循环接口。 IOLoop.current
类方法提供了对应于正在运行的 asyncio
事件循环的 IOLoop
实例。
IOLoop 对象¶
- class tornado.ioloop.IOLoop(*args: Any, **kwargs: Any)[source]¶
一个 I/O 事件循环。
从 Tornado 6.0 开始,
IOLoop
是asyncio
事件循环的包装器。简单 TCP 服务器的示例用法
import asyncio import errno import functools import socket import tornado from tornado.iostream import IOStream async def handle_connection(connection, address): stream = IOStream(connection) message = await stream.read_until_close() print("message from client:", message.decode().strip()) def connection_ready(sock, fd, events): while True: try: connection, address = sock.accept() except BlockingIOError: return connection.setblocking(0) io_loop = tornado.ioloop.IOLoop.current() io_loop.spawn_callback(handle_connection, connection, address) async def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setblocking(0) sock.bind(("", 8888)) sock.listen(128) io_loop = tornado.ioloop.IOLoop.current() callback = functools.partial(connection_ready, sock) io_loop.add_handler(sock.fileno(), callback, io_loop.READ) await asyncio.Event().wait() if __name__ == "__main__": asyncio.run(main())
大多数应用程序不应尝试直接构建
IOLoop
,而应初始化asyncio
事件循环并使用IOLoop.current()
。在某些情况下,例如在测试框架中初始化要在辅助线程中运行的IOLoop
时,使用IOLoop(make_current=False)
构建IOLoop
可能更合适。一般来说,
IOLoop
无法在 fork 中存活或以任何方式跨进程共享。当使用多个进程时,每个进程都应该创建自己的IOLoop
,这也意味着任何依赖IOLoop
的对象(如AsyncHTTPClient
)也必须在子进程中创建。作为指导方针,任何启动进程的内容(包括tornado.process
和multiprocessing
模块)应该尽早执行,理想情况下是应用程序在加载其配置后执行的第一件事,并且在任何调用IOLoop.start
或asyncio.run
之前执行。在版本 4.2 中变更: 在
IOLoop
构造函数中添加了make_current
关键字参数。在版本 6.3 中变更:
make_current=True
现在是创建 IOLoop 时默认的 - 以前默认是在没有当前事件循环的情况下将其设置为当前事件循环。
运行 IOLoop¶
- static IOLoop.current() IOLoop [source]¶
- static IOLoop.current(instance: bool = True) Optional[IOLoop]
返回当前线程的
IOLoop
。如果
IOLoop
正在运行或已通过make_current
标记为当前,则返回该实例。如果没有当前IOLoop
并且instance
为 true,则创建一个。在版本 4.1 中变更: 添加了
instance
参数来控制回退到IOLoop.instance()
。在版本 5.0 中变更: 在 Python 3 中,当前
IOLoop
的控制权委派给了asyncio
,并且此方法和其他方法作为传透访问器。instance
参数现在控制是否在没有IOLoop
时自动创建它,而不是我们是否回退到IOLoop.instance()
(现在是此方法的别名)。instance=False
已弃用,因为即使我们没有创建IOLoop
,此方法也可能会初始化 asyncio 循环。自版本 6.2 起弃用: 在没有运行
asyncio
事件循环时调用IOLoop.current()
已被弃用。
- IOLoop.make_current() None [source]¶
使当前线程使用此
IOLoop
。当
IOLoop
启动时,它会自动成为其线程的当前循环,但有时在启动IOLoop
之前显式调用make_current
会很有用,这样启动时运行的代码就可以找到正确的实例。版本 5.0 中变更: 此方法还会设置当前的
asyncio
事件循环。版本 6.2 起弃用: 通过 Tornado 设置和清除当前事件循环已弃用。如果您需要此功能,请改用
asyncio.set_event_loop
。
- static IOLoop.clear_current() None [source]¶
清除当前线程的
IOLoop
。主要供测试框架在测试之间使用。
版本 5.0 中变更: 此方法还会清除当前的
asyncio
事件循环。版本 6.2 起弃用。
- IOLoop.stop() None [source]¶
停止 I/O 循环。
如果事件循环当前没有运行,则下次调用
start()
将立即返回。请注意,即使在调用
stop
后,IOLoop
也不会完全停止,直到IOLoop.start
也返回。在调用stop
之前安排的一些工作可能仍会在IOLoop
关闭之前运行。
- IOLoop.run_sync(func: Callable, timeout: Optional[float] = None) Any [source]¶
启动
IOLoop
,运行给定的函数,然后停止循环。该函数必须返回一个可等待对象或
None
。如果函数返回一个可等待对象,则IOLoop
将运行,直到该可等待对象被解析(并且run_sync()
将返回该可等待对象的返回值)。如果它引发异常,则IOLoop
将停止,并且异常将重新抛出给调用者。仅限关键字的参数
timeout
可用于设置函数的最大持续时间。如果超时过期,将引发asyncio.TimeoutError
。此方法用于在
main()
函数中允许异步调用。async def main(): # do stuff... if __name__ == '__main__': IOLoop.current().run_sync(main)
版本 4.3 中变更: 现在返回非
None
、非可等待值将被视为错误。版本 5.0 中变更: 如果发生超时,则将取消
func
协程。版本 6.2 中变更:
tornado.util.TimeoutError
现在是asyncio.TimeoutError
的别名。
- IOLoop.close(all_fds: bool = False) None [source]¶
关闭
IOLoop
,释放使用的任何资源。如果
all_fds
为真,则将关闭在 IOLoop 上注册的所有文件描述符(不仅仅是IOLoop
本身创建的描述符)。许多应用程序只会使用一个
IOLoop
,该循环在整个进程的生命周期中运行。在这种情况下,无需关闭IOLoop
,因为所有内容都将在进程退出时清理。IOLoop.close
主要用于单元测试等场景,这些场景会创建和销毁大量的IOLoops
。必须完全停止
IOLoop
才能关闭它。这意味着必须调用IOLoop.stop()
,并且必须允许IOLoop.start()
返回,然后才能尝试调用IOLoop.close()
。因此,调用close
通常会出现在调用start
之后,而不是在调用stop
附近。版本 3.1 中变更: 如果
IOLoop
实现支持将非整数对象用作“文件描述符”,那么当all_fds
为真时,这些对象将调用其close
方法。
- static IOLoop.instance() IOLoop [source]¶
IOLoop.current()
的已弃用别名。在版本 5.0 中变更: 以前,此方法返回一个全局单例
IOLoop
,与current()
返回的每个线程IOLoop
相反。在几乎所有情况下,两者都是相同的(当它们不同时,通常用于从非 Tornado 线程通信回主线程的IOLoop
)。asyncio
中不存在这种区别,因此为了便于与该包的集成,instance()
已更改为current()
的别名。使用instance()
的跨线程通信方面的应用程序应改为将自己的全局变量设置为指向他们想要使用的IOLoop
。自版本 5.0 起已弃用。
- IOLoop.install() None [source]¶
make_current()
的已弃用别名。在版本 5.0 中变更: 以前,此方法会将此
IOLoop
设置为IOLoop.instance()
使用的全局单例。现在instance()
是current()
的别名,install()
是make_current()
的别名。自版本 5.0 起已弃用。
- static IOLoop.clear_instance() None [source]¶
clear_current()
的已弃用别名。在版本 5.0 中变更: 以前,此方法会清除
IOLoop
,该IOLoop
被IOLoop.instance()
用作全局单例。现在instance()
是current()
的别名,clear_instance()
是clear_current()
的别名。自版本 5.0 起已弃用。
I/O 事件¶
- IOLoop.add_handler(fd: int, handler: Callable[[int, int], None], events: int) None [source]¶
- IOLoop.add_handler(fd: _S, handler: Callable[[_S, int], None], events: int) None
注册给定的处理程序以接收
fd
的给定事件。fd
参数可以是整数文件描述符,也可以是具有fileno()
和close()
方法的文件类对象。events
参数是常量IOLoop.READ
、IOLoop.WRITE
和IOLoop.ERROR
的按位或。当事件发生时,将运行
handler(fd, events)
。在版本 4.0 中变更: 添加了除了原始文件描述符之外还可以传递文件类对象的功能。
回调和超时¶
- IOLoop.add_callback(callback: Callable, *args: Any, **kwargs: Any) None [source]¶
在下一次 I/O 循环迭代中调用给定的回调。
可以从任何线程在任何时间调用此方法,除了信号处理程序。注意,这是
IOLoop
中唯一保证线程安全的方法;与IOLoop
的所有其他交互都必须在该IOLoop
的线程中完成。add_callback()
可用于将控制权从其他线程转移到IOLoop
的线程。
- IOLoop.add_callback_from_signal(callback: Callable, *args: Any, **kwargs: Any) None [source]¶
在下一次 I/O 循环迭代中调用给定的回调。
旨在安全地从 Python 信号处理程序使用;不应该以其他方式使用。
从版本 6.4 开始弃用: 请改用
asyncio.AbstractEventLoop.add_signal_handler
。此方法据怀疑自 Tornado 5.0 以来一直存在问题,将在版本 7.0 中移除。
- IOLoop.add_future(future: Union[Future[_T], concurrent.futures.Future[_T]], callback: Callable[[Future[_T]], None]) None [source]¶
在给定的
Future
完成时,在IOLoop
上调度一个回调。回调使用一个参数调用,即
Future
。此方法只接受
Future
对象,而不是其他可等待对象(与 Tornado 中大多数情况下两者可以互换不同)。
- IOLoop.add_timeout(deadline: Union[float, timedelta], callback: Callable, *args: Any, **kwargs: Any) object [source]¶
在 I/O 循环中,在时间
deadline
运行callback
。返回一个不透明句柄,它可以传递给
remove_timeout
以取消。deadline
可以是表示时间的数字(与IOLoop.time
使用相同的刻度,通常是time.time
),也可以是datetime.timedelta
对象,表示相对于当前时间的截止时间。自 Tornado 4.0 以来,call_later
是相对情况下的更方便的替代方案,因为它不需要 timedelta 对象。注意,从其他线程调用
add_timeout
不安全。相反,必须使用add_callback
将控制权转移到IOLoop
的线程,然后从那里调用add_timeout
。IOLoop 的子类必须实现
add_timeout
或call_at
;每种方法的默认实现将调用另一种方法。call_at
通常更容易实现,但希望与 Tornado 4.0 之前的版本保持兼容的子类必须使用add_timeout
而不是它。版本 4.0 中的更改: 现在将
*args
和**kwargs
传递给回调。
- IOLoop.call_at(when: float, callback: Callable, *args: Any, **kwargs: Any) object [source]¶
在由
when
指定的绝对时间运行callback
。when
必须是一个使用与IOLoop.time
相同参考点的数字。返回一个不透明的句柄,可以传递给
remove_timeout
以取消。请注意,与asyncio
中同名方法不同,返回的对象没有cancel()
方法。有关线程安全性 and 子类化的说明,请参见
add_timeout
。版本 4.0 中新增。
- IOLoop.call_later(delay: float, callback: Callable, *args: Any, **kwargs: Any) object [source]¶
在
delay
秒后运行callback
。返回一个不透明的句柄,可以传递给
remove_timeout
以取消。请注意,与asyncio
中同名方法不同,返回的对象没有cancel()
方法。有关线程安全性 and 子类化的说明,请参见
add_timeout
。版本 4.0 中新增。
- IOLoop.remove_timeout(timeout: object) None [source]¶
取消挂起的超时。
参数是
add_timeout
返回的句柄。即使回调已经运行,调用remove_timeout
也是安全的。
- IOLoop.spawn_callback(callback: Callable, *args: Any, **kwargs: Any) None [source]¶
在下次 IOLoop 迭代时调用给定的回调。
从 Tornado 6.0 开始,此方法等效于
add_callback
。版本 4.0 中新增。
- IOLoop.run_in_executor(executor: Optional[Executor], func: Callable[[...], _T], *args: Any) Future[_T] [source]¶
在
concurrent.futures.Executor
中运行函数。如果executor
为None
,则将使用 IO 循环的默认执行器。使用
functools.partial
将关键字参数传递给func
。版本 5.0 中新增。
- IOLoop.set_default_executor(executor: Executor) None [source]¶
设置用于
run_in_executor()
的默认执行器。版本 5.0 中新增。
- IOLoop.time() float [source]¶
返回根据
IOLoop
的时钟获得的当前时间。返回值是一个相对于过去某个未指定时间的浮点数。
历史上,IOLoop 可以定制以使用例如
time.monotonic
而不是time.time
,但这目前不支持,因此此方法等效于time.time
。
- class tornado.ioloop.PeriodicCallback(callback: Callable[[], Optional[Awaitable]], callback_time: Union[timedelta, float], jitter: float = 0)[source]¶
定期调度给定回调函数。
当
callback_time
为浮点数时,回调函数每callback_time
毫秒调用一次。请注意,超时时间以毫秒为单位,而 Tornado 中大多数其他与时间相关的函数使用秒为单位。callback_time
可以选择性地设置为datetime.timedelta
对象。如果指定了
jitter
,则每次回调时间将在jitter * callback_time
毫秒的窗口内随机选择。抖动可以用来减少具有相似周期的事件的对齐。抖动值为 0.1 表示允许回调时间有 10% 的变化。窗口以callback_time
为中心,因此在给定间隔内的总调用次数不应受到添加抖动的显著影响。如果回调函数的运行时间超过了
callback_time
毫秒,则将跳过后续调用以恢复正常调度。创建
PeriodicCallback
后,必须调用start
。版本 5.0 中的变化: 已移除
io_loop
参数(自版本 4.1 起弃用)。版本 5.1 中的变化: 添加了
jitter
参数。版本 6.2 中的变化: 如果
callback
参数是协程,并且回调函数的运行时间超过了callback_time
,则将跳过后续调用。以前,这仅适用于普通函数,而不适用于协程,协程对于PeriodicCallback
是“一次性”的。除了以前支持的数字毫秒值外,
callback_time
参数现在也接受datetime.timedelta
对象。- is_running() bool [source]¶
如果此
PeriodicCallback
已启动,则返回True
。版本 4.1 中的新功能。