Tornado 主事件循环
用于非阻塞套接字的 I/O 事件循环。
在 Tornado 6.0 中,IOLoop
是 asyncio
事件循环的包装器,由于历史原因,接口略有不同。 应用程序可以直接使用 IOLoop
接口或底层asyncio
事件循环(除非需要与旧版本的 Tornado 兼容,在这种情况下必须使用 IOLoop
)。
典型的应用程序将使用单个 IOLoop
对象,通过IOLoop.current
类方法访问。IOLoop.start
方法(或等效的 asyncio.AbstractEventLoop.run_forever
)通常应在 main()
函数的末尾调用。 非典型应用程序可能使用多个 IOLoop
,例如每个线程或每个单元测试用例一个 IOLoop
。
IOLoop 对象
class tornado.ioloop.IOLoop
一个 I/O 事件循环。
从 Tornado 6.0 开始,IOLoop
是 asyncio
事件循环的包装器。
简单 TCP 服务器的示例用法:
import errno
import functools
import socket
import tornado.ioloop
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)
if __name__ == '__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)
io_loop.start()
默认情况下,新构建的 IOLoop
成为线程的当前 IOLoop
,除非已经存在当前 IOLoop
。 此行为可以通过 IOLoop
构造函数的make_current
参数来控制:如果 make_current=True
,新的 IOLoop
将始终尝试成为当前实例,如果已经存在当前实例,则会引发错误。 如果 make_current=False
,新的 IOLoop
将不会尝试成为当前的。
一般来说,IOLoop
不能在分叉中存活或以任何方式跨进程共享。 当使用多个进程时,每个进程都应该创建自己的 IOLoop
,这也意味着任何依赖于 IOLoop
的对象(例如 AsyncHTTPClient
)也必须在子进程中创建。 作为指导,任何启动进程(包括 tornado.process
和 multiprocessing
模块)都应该尽早启动,最好是应用程序在 main()
中加载其配置后首先执行的操作。
在 4.2 版更改: 向 IOLoop
构造函数添加了 make_current
关键字参数。
在 5.0 版更改: 默认使用 asyncio
事件循环。 IOLoop.configure
方法不能在 Python 3 上使用,除非冗余指定 asyncio
事件循环。
运行IOLoop
static IOLoop.current(instance: bool = True) → Optional[tornado.ioloop.IOLoop]
返回当前线程的 IOLoop
。
如果 IOLoop
当前正在运行或已被 make_current
标记为当前,则返回instance
。 如果没有当前 IOLoop
并且instance
为真,则创建一个。
在 4.1 版更改: 添加了实例参数来控制对 IOLoop.instance()
的回退。
在 5.0 版更改:在 Python 3 上,当前 IOLoop
的控制权委托给 asyncio
,并使用此方法和其他方法作为传递访问器。 instance
参数现在控制在没有 IOLoop
时是否自动创建,而不是我们是否回退到 IOLoop.instance()
(现在是此方法的别名)。 instance=False
已弃用,因为即使我们不创建 IOLoop
,此方法也可能会初始化 asyncio
循环。
IOLoop.make_current() → None
使其成为当前线程的 IOLoop
。
IOLoop
在启动时自动成为其线程的当前线程,但有时在启动 IOLoop
之前显式调用 make_current
很有用,以便在启动时运行的代码可以找到正确的实例。
在 4.1 版更改: 在没有当前 IOLoop
时创建的 IOLoop
将自动变为当前。
在 5.0 版更改: 此方法还设置当前的 asyncio
事件循环。
static IOLoop.clear_current() → None
清除当前线程的 IOLoop
。
主要供测试框架在测试之间使用。
在 5.0 版更改: 此方法还清除当前的 asyncio
事件循环。
IOLoop.start() → None
启动I/O
循环。
循环将一直运行,直到其中一个回调调用 stop()
,这将使循环在当前事件迭代完成后停止。
IOLoop.stop() → None
停止 I/O
循环。
如果事件循环当前未运行,则对 start()
的下一次调用将立即返回。
请注意,即使在调用 stop
之后,IOLoop
也不会完全停止,直到 IOLoop.start
也返回。 在调用停止之前安排的一些工作可能在 IOLoop
关闭之前仍然运行。
IOLoop.run_sync(func: Callable, timeout: Optional[float] = None) → Any
启动 IOLoop
,运行给定函数,然后停止循环。
该函数必须返回一个可等待对象或无。 如果函数返回一个可等待对象,IOLoop
将一直运行,直到解决了可等待对象(并且 run_sync()
将返回可等待对象的结果)。 如果引发异常,IOLoop
将停止,异常将重新引发给调用者。
仅关键字参数 timeout
可用于设置函数的最大持续时间。 如果超时到期,则会引发 tornado.util.TimeoutError
。
此方法对于允许在 main()
函数中进行异步调用很有用:
async def main():
# do stuff...
if __name__ == '__main__':
IOLoop.current().run_sync(main)
IOLoop.close(all_fds: bool = False) → None
关闭 IOLoop
,释放所有使用的资源。
如果 all_fds
为真,则在 IOLoop
上注册的所有文件描述符都将被关闭(不仅仅是 IOLoop
本身创建的文件描述符)。
许多应用程序只会使用一个在整个进程生命周期内运行的 IOLoop
。 在这种情况下,不需要关闭 IOLoop
,因为当进程退出时,所有内容都会被清理。 IOLoop.close
主要用于单元测试等场景,创建和销毁大量的IOLoop
。
IOLoop
必须完全停止才能关闭。 这意味着在尝试调用 IOLoop.close()
之前必须调用 IOLoop.stop()
并且必须允许 IOLoop.start()
返回。 因此,close
调用通常会出现在 start
调用之后,而不是在 stop
调用附近。
在 3.1 版更改:如果 IOLoop
实现支持“文件描述符”的非整数对象,则当 all_fds
为 true
时,这些对象将具有其 close
方法。
static IOLoop.instance() → tornado.ioloop.IOLoop
IOLoop.current()
的已弃用别名。
在 5.0 版更改: 以前,此方法返回一个全局单例 IOLoop
,与 current()
返回的每个线程 IOLoop
形成对比。 在几乎所有情况下,两者都是相同的(当它们不同时,它通常用于非 Tornado 线程与主线程的 IOLoop
进行通信)。 这种区别在 asyncio
中不存在,因此为了便于与该包集成,instance()
已更改为 current()
的别名。 使用 instance()
的跨线程通信方面的应用程序应该将自己的全局变量设置为指向他们想要使用的 IOLoop
。
自 5.0 版起已弃用。
IOLoop.install() → None
不推荐使用 make_current()
的别名。
在 5.0 版更改: 以前,此方法将此 IOLoop
设置为IOLoop.instance()
使用的全局单例。 现在 instance()
是 current()
的别名,install()
是 make_current()
的别名。
自 5.0 版起已弃用。
static IOLoop.clear_instance() → None
不推荐使用 clear_current()
的别名。
在 5.0 版更改: 以前,此方法将清除 IOLoop.instance()
用作全局单例的 IOLoop
。 现在 instance()
是 current()
的别名,clear_instance()
是 clear_current()
的别名。
自 5.0 版起已弃用。
I/O事件
IOLoop.add_handler(fd: Union[int, tornado.ioloop._Selectable], handler: Callable[[...], None], events: int) → None
注册给定的处理程序以接收fd
的给定事件。
fd
参数可以是整数文件描述符,也可以是具有 fileno()
和 close()
方法的类文件对象。
events
参数是常量 IOLoop.READ
、IOLoop.WRITE
和 IOLoop.ERROR
的按位或。
当事件发生时,将运行 handler(fd, events)
。
在 4.0 版更改: 除了原始文件描述符之外,还添加了传递类似文件的对象的能力。
IOLoop.update_handler(fd: Union[int, tornado.ioloop._Selectable], events: int) → None
改变我们监听fd
上的事件。
在 4.0 版更改: 除了原始文件描述符之外,还添加了传递类似文件的对象的能力。
IOLoop.remove_handler(fd: Union[int, tornado.ioloop._Selectable]) → None
停止监听fd
上的事件。
在 4.0 版更改: 除了原始文件描述符之外,还添加了传递类似文件的对象的能力。
回调和超时
IOLoop.add_callback(callback: Callable, *args, **kwargs) → None
在下一次 I/O
循环迭代中调用给定的回调。
任何时候从任何线程调用此方法都是安全的,信号处理程序除外。 请注意,这是 IOLoop
中唯一保证线程安全的方法; 与 IOLoop
的所有其他交互都必须从该 IOLoop
的线程中完成。 add_callback()
可用于控制从其他线程转移到 IOLoop
的线程。
IOLoop.add_callback_from_signal(callback: Callable, *args, **kwargs) → None
在下一次 I/O
循环迭代中调用给定的回调。
可从 Python 信号处理程序安全使用; 否则不应使用。
IOLoop.add_future(future: Union[Future[_T], concurrent.futures.Future[_T]], callback: Callable[[Future[_T]], None]) → None
当给定的 Future
完成时,在 IOLoop
上安排回调。
使用一个参数 Future
调用回调。
此方法仅接受 Future
对象而不接受其他可等待对象(与大多数 Tornado 不同,两者可互换)。
IOLoop.add_timeout(deadline: Union[float, datetime.timedelta], callback: Callable[[...], None], *args, **kwargs) → object
在 I/O
循环的时间截止日期运行回调。
返回一个不透明的句柄,可以传递给 remove_timeout
以取消。
截止日期可以是一个表示时间的数字(与 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[[...], None], *args, **kwargs) → object
在 when
指定的绝对时间运行callback
。
when
必须是使用与 IOLoop.time
相同的参考点的数字。
返回一个不透明的句柄,可以传递给 remove_timeout
以取消。 注意,与同名的asyncio
方法不同,返回的对象没有 cancel()
方法。
IOLoop.call_later(delay: float, callback: Callable[[...], None], *args, **kwargs) → object
在delay
过去后运行callback
。
返回一个不透明的句柄,可以传递给 remove_timeout
以取消。 注意,与同名的 asyncio
方法不同,返回的对象没有 cancel()
方法。
IOLoop.remove_timeout(timeout: object) → None
取消挂起的超时。
参数是 add_timeout
返回的句柄。 即使回调已经运行,调用 remove_timeout
也是安全的。
IOLoop.spawn_callback(callback: Callable, *args, **kwargs) → None
在下一次 IOLoop
迭代中调用给定的回调。
从 Tornado 6.0 开始,此方法等效于 add_callback
。
IOLoop.run_in_executor(executor: Optional[concurrent.futures._base.Executor], func: Callable[[...], _T], *args) → Awaitable[_T]
在 concurrent.futures.Executor
中运行一个函数。 如果 executor
为 None
,将使用 IOLoop默认的 executor
。
使用 functools.partial
将关键字参数传递给 func
。
IOLoop.set_default_executor(executor: concurrent.futures._base.Executor) → None
设置与 run_in_executor()
一起使用的默认执行程序。
IOLoop.time() → float
根据 IOLoop
的时钟返回当前时间。
返回值是相对于过去未指定时间的浮点数。
IOLoop
可以定制为使用例如 time.monotonic
代替 time.time
,但目前不支持此方法,因此此方法等效于 time.time
class tornado.ioloop.PeriodicCallback(callback: Callable[[], None], callback_time: float, jitter: float = 0)。
安排定期调用给定的回调。
每个callback_time
毫秒调用一次回调。 请注意,超时以毫秒为单位,而 Tornado 中大多数其他与时间相关的函数使用秒。
如果指定了jitter
,每个回调时间将在一个jitter * callback_time
毫秒的窗口内随机选择。 抖动可用于减少具有相似周期的事件的对齐。 0.1 的抖动意味着允许 10% 的回调时间变化。 窗口以 callback_time
为中心,因此给定时间间隔内的调用总数不应受到添加抖动的显着影响。
如果回调运行时间超过 callback_time
毫秒,则将跳过后续调用以按计划返回。
start
必须在 PeriodicCallback
创建后调用。
在 5.0 版中更改: io_loop
参数(自 4.1 版以来已弃用)已被删除。
在 5.1 版更改: 添加了 jitter
参数。
start() → None
启动计时器。
stop() → None
停止计时器。
is_running() → bool
如果此 PeriodicCallback
已启动,则返回 True
。