18.5.1. 基本事件循环 — Python 文档
18.5.1. 基本事件循环
源代码: :source:`Lib/asyncio/events.py`
事件循环是 asyncio 提供的中央执行设备。 它提供多种设施,包括:
- class asyncio.BaseEventLoop
- 这个类是一个实现细节。 它是 AbstractEventLoop 的子类,并且可能是在 asyncio 中找到的具体事件循环实现的基类。 不应直接使用; 改用 AbstractEventLoop。
BaseEventLoop
不应被第三方代码子类化; 内部接口不稳定。
- class asyncio.AbstractEventLoop
事件循环的抽象基类。
此类是 不是线程安全的 。
18.5.1.1. 运行事件循环
- AbstractEventLoop.run_forever()
运行直到 stop() 被调用。 如果在调用 run_forever() 之前调用 stop(),这将轮询一次 I/O 选择器,超时为零,运行所有为响应 I/O 事件而安排的回调(以及那些已经安排好的),然后退出。 如果在 run_forever() 运行时调用 stop(),这将运行当前批次的回调,然后退出。 请注意,在这种情况下,回调安排的回调将不会运行; 它们将在下次调用 run_forever() 时运行。
在 3.5.1 版中更改。
- AbstractEventLoop.run_until_complete(future)
运行直到 Future 完成。
如果参数是 协程对象 ,则由 ensure_future() 包裹。
返回 Future 的结果,或引发其异常。
- AbstractEventLoop.is_running()
- 返回事件循环的运行状态。
- AbstractEventLoop.stop()
停止运行事件循环。
这会导致 run_forever() 在下一个合适的机会退出(有关更多详细信息,请参见此处)。
在 3.5.1 版中更改。
- AbstractEventLoop.is_closed()
如果事件循环已关闭,则返回
True
。版本 3.4.2 中的新功能。
- AbstractEventLoop.close()
关闭事件循环。 循环不能运行。 挂起的回调将丢失。
这会清除队列并关闭执行程序,但不会等待执行程序完成。
这是幂等且不可逆的。 在此之后不应调用其他方法。
18.5.1.2. 通话
大多数 asyncio 函数不接受关键字。 如果要将关键字传递给回调,请使用 functools.partial()。 例如,loop.call_soon(functools.partial(print, "Hello", flush=True))
将调用 print("Hello", flush=True)
。
笔记
functools.partial() 比 lambda
函数更好,因为 asyncio 可以检查 functools.partial() 对象在调试模式下显示参数,而 lambda
函数的代表性较差。
- AbstractEventLoop.call_soon(callback, \*args)
安排尽快调用回调。 当控制返回到事件循环时,在 call_soon() 返回后调用回调。
这作为 FIFO 队列运行,回调按它们注册的顺序调用。 每个回调只会被调用一次。
回调之后的任何位置参数都将在调用时传递给回调。
返回一个 asyncio.Handle 的实例,可用于取消回调。
- AbstractEventLoop.call_soon_threadsafe(callback, \*args)
像 call_soon(),但线程安全。
请参阅文档的 并发和多线程 部分。
18.5.1.3. 延迟呼叫
事件循环有自己的内部时钟来计算超时。 使用哪个时钟取决于(特定于平台的)事件循环实现; 理想情况下,它是一个单调时钟。 这通常是与 time.time() 不同的时钟。
笔记
超时(相对 延迟 或绝对 when)不应超过 1 天。
- AbstractEventLoop.call_later(delay, callback, *args)
安排 callback 在给定的 delay 秒(整数或浮点数)后调用。
返回一个 asyncio.Handle 的实例,可用于取消回调。
callback 每次调用 call_later() 时只会被调用一次。 如果两个回调被安排在完全相同的时间,则未定义将首先调用哪个。
可选的位置 args 将在调用时传递给回调。 如果您希望使用一些命名参数调用回调,请使用闭包或 functools.partial()。
- AbstractEventLoop.call_at(when, callback, *args)
使用与 AbstractEventLoop.time() 相同的时间参考,安排 callback 在给定的绝对时间戳 when(整数或浮点数)上调用。
此方法的行为与 call_later() 相同。
返回一个 asyncio.Handle 的实例,可用于取消回调。
- AbstractEventLoop.time()
- 根据事件循环的内部时钟,以 float 值的形式返回当前时间。
也可以看看
asyncio.sleep()
功能。
18.5.1.4. 期货
- AbstractEventLoop.create_future()
创建一个附加到循环的 asyncio.Future 对象。
这是在 asyncio 中创建期货的首选方式,因为事件循环实现可以提供 Future 类的替代实现(具有更好的性能或检测)。
版本 3.5.2 中的新功能。
18.5.1.5. 任务
- AbstractEventLoop.create_task(coro)
安排 协程对象 的执行:将其包装在未来。 返回一个 Task 对象。
第三方事件循环可以使用它们自己的 Task 子类来实现互操作性。 在这种情况下,结果类型是 Task 的子类。
这个方法是在 Python 3.4.2 中添加的。 使用 async() 函数也支持较旧的 Python 版本。
版本 3.4.2 中的新功能。
- AbstractEventLoop.set_task_factory(factory)
设置一个由 AbstractEventLoop.create_task() 使用的任务工厂。
如果 factory 是
None
将设置默认任务工厂。如果 factory 是一个 callable,它应该有一个匹配
(loop, coro)
的签名,其中 loop 将是对活动事件循环的引用,[ X157X]coro 将是一个协程对象。 可调用对象必须返回 asyncio.Future 兼容对象。版本 3.4.4 中的新功能。
- AbstractEventLoop.get_task_factory()
返回一个任务工厂,如果使用默认工厂,则返回
None
。版本 3.4.4 中的新功能。
18.5.1.6. 创建连接
18.5.1.7. 创建监听连接
18.5.1.8. 观察文件描述符
在带有 SelectorEventLoop 的 Windows 上,仅支持套接字句柄(例如:不支持管道文件描述符)。
在带有 ProactorEventLoop 的 Windows 上,不支持这些方法。
- AbstractEventLoop.add_reader(fd, callback, \*args)
开始观察文件描述符以获取读取可用性,然后使用指定参数调用 回调 。
- AbstractEventLoop.remove_reader(fd)
- 停止监视文件描述符以获取读取可用性。
- AbstractEventLoop.add_writer(fd, callback, \*args)
开始观察文件描述符以获取写入可用性,然后使用指定参数调用 回调 。
- AbstractEventLoop.remove_writer(fd)
- 停止监视文件描述符以获取写入可用性。
watch a file descriptor for read events 示例使用低级 AbstractEventLoop.add_reader() 方法注册套接字的文件描述符。
18.5.1.9. 低级套接字操作
18.5.1.10。 解析主机名
18.5.1.11。 连接管道
在带有 SelectorEventLoop 的 Windows 上,不支持这些方法。 使用 ProactorEventLoop 在 Windows 上支持管道。
也可以看看
AbstractEventLoop.subprocess_exec()
和 AbstractEventLoop.subprocess_shell()
方法。
18.5.1.12。 UNIX 信号
可用性:仅限 UNIX。
- AbstractEventLoop.add_signal_handler(signum, callback, \*args)
为信号添加处理程序。
如果信号编号无效或无法捕获,则引发 ValueError。 如果设置处理程序出现问题,则引发 RuntimeError。
- AbstractEventLoop.remove_signal_handler(sig)
删除信号的处理程序。
如果删除了信号处理程序,则返回
True
,否则返回False
。
18.5.1.13。 执行者
在 Executor(线程池或进程池)中调用函数。 默认情况下,事件循环使用线程池执行器 (ThreadPoolExecutor)。
- AbstractEventLoop.set_default_executor(executor)
- 设置
run_in_executor()
使用的默认执行器。
18.5.1.14。 错误处理 API
允许自定义如何在事件循环中处理异常。
- AbstractEventLoop.set_exception_handler(handler)
将 handler 设置为新的事件循环异常处理程序。
如果 handler 是
None
,将设置默认异常处理程序。如果 handler 是一个可调用对象,它应该具有与
(loop, context)
匹配的签名,其中loop
将是对活动事件循环的引用,context
将是一个dict
对象(有关上下文的详细信息,请参阅 call_exception_handler() 文档)。
- AbstractEventLoop.get_exception_handler()
返回异常处理程序,如果正在使用默认处理程序,则返回
None
。版本 3.5.2 中的新功能。
- AbstractEventLoop.default_exception_handler(context)
默认异常处理程序。
当发生异常并且未设置异常处理程序时调用它,并且可以由想要遵循默认行为的自定义异常处理程序调用。
context 参数的含义与 call_exception_handler() 中的含义相同。
- AbstractEventLoop.call_exception_handler(context)
调用当前事件循环异常处理程序。
context 是一个
dict
对象,包含以下键(稍后可能会引入新键):'message':错误信息;
“异常”(可选):异常对象;
'future'(可选):asyncio.Future 实例;
'handle'(可选):asyncio.Handle 实例;
'protocol'(可选):Protocol 实例;
'transport'(可选):Transport 实例;
'socket'(可选):socket.socket 实例。
笔记
注意:此方法不应在子类事件循环中重载。 对于任何自定义异常处理,请使用 set_exception_handler() 方法。
18.5.1.15。 调试模式
- AbstractEventLoop.get_debug()
获取事件循环的调试模式(bool)。
如果环境变量 PYTHONASYNCIODEBUG 设置为非空字符串,则默认值为
True
,否则为False
。版本 3.4.2 中的新功能。
- AbstractEventLoop.set_debug(enabled: bool)
设置事件循环的调试模式。
版本 3.4.2 中的新功能。
18.5.1.16。 服务器
- class asyncio.Server
服务器侦听套接字。
由
AbstractEventLoop.create_server()
方法和start_server()
函数创建的对象。 不要直接实例化类。- close()
停止服务:关闭监听套接字并将 sockets 属性设置为
None
。代表现有传入客户端连接的套接字保持打开状态。
服务器异步关闭,使用
wait_closed()
协程等待服务器关闭。
- sockets
服务器正在侦听的 socket.socket 对象列表,如果服务器关闭,则为
None
。
18.5.1.17。 处理
- class asyncio.Handle
- AbstractEventLoop.call_soon()、AbstractEventLoop.call_soon_threadsafe()、AbstractEventLoop.call_later()和.AbstractEventLoop.call_later()返回的回调包装对象)。
- cancel()
- 取消通话。 如果回调已被取消或执行,则此方法无效。
18.5.1.18。 事件循环示例
18.5.1.18.1。 Hello World with call_soon()
使用 AbstractEventLoop.call_soon() 方法安排回调的示例。 回调显示 "Hello World"
然后停止事件循环:
import asyncio
def hello_world(loop):
print('Hello World')
loop.stop()
loop = asyncio.get_event_loop()
# Schedule a call to hello_world()
loop.call_soon(hello_world, loop)
# Blocking call interrupted by loop.stop()
loop.run_forever()
loop.close()
18.5.1.18.2. 使用 call_later() 显示当前日期
每秒显示当前日期的回调示例。 回调使用 AbstractEventLoop.call_later() 方法在 5 秒内重新调度自身,然后停止事件循环:
import asyncio
import datetime
def display_date(end_time, loop):
print(datetime.datetime.now())
if (loop.time() + 1.0) < end_time:
loop.call_later(1, display_date, end_time, loop)
else:
loop.stop()
loop = asyncio.get_event_loop()
# Schedule the first call to display_date()
end_time = loop.time() + 5.0
loop.call_soon(display_date, end_time, loop)
# Blocking call interrupted by loop.stop()
loop.run_forever()
loop.close()
18.5.1.18.3。 观察文件描述符以获取读取事件
等到文件描述符使用 AbstractEventLoop.add_reader() 方法接收到一些数据,然后关闭事件循环:
import asyncio
try:
from socket import socketpair
except ImportError:
from asyncio.windows_utils import socketpair
# Create a pair of connected file descriptors
rsock, wsock = socketpair()
loop = asyncio.get_event_loop()
def reader():
data = rsock.recv(100)
print("Received:", data.decode())
# We are done: unregister the file descriptor
loop.remove_reader(rsock)
# Stop the event loop
loop.stop()
# Register the file descriptor for read event
loop.add_reader(rsock, reader)
# Simulate the reception of data from the network
loop.call_soon(wsock.send, 'abc'.encode())
# Run the event loop
loop.run_forever()
# We are done, close sockets and the event loop
rsock.close()
wsock.close()
loop.close()
也可以看看
使用协议注册一个打开的套接字以等待数据 示例使用由 AbstractEventLoop.create_connection()
方法创建的低级协议。
注册一个打开的套接字以使用流等待数据 示例使用由 open_connection()
函数在协程中创建的高级流。
18.5.1.18.4。 为 SIGINT 和 SIGTERM 设置信号处理程序
使用 AbstractEventLoop.add_signal_handler() 方法为信号 SIGINT
和 SIGTERM
注册处理程序:
import asyncio
import functools
import os
import signal
def ask_exit(signame):
print("got signal %s: exit" % signame)
loop.stop()
loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
loop.add_signal_handler(getattr(signal, signame),
functools.partial(ask_exit, signame))
print("Event loop running forever, press Ctrl+C to interrupt.")
print("pid %s: send SIGINT or SIGTERM to exit." % os.getpid())
try:
loop.run_forever()
finally:
loop.close()
此示例仅适用于 UNIX。