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 中的新功能。