运行器¶
本节概述了运行 asyncio 代码的高级 asyncio 原语。
它们建立在 事件循环 之上,目的是简化常见广泛场景中的异步代码使用。
运行 asyncio 程序¶
- asyncio.run(coro, *, debug=None, loop_factory=None)¶
执行 协程 coro 并返回结果。
此函数运行传递的协程,负责管理 asyncio 事件循环、完成异步生成器和关闭执行器。
当另一个 asyncio 事件循环在同一线程中运行时,无法调用此函数。
如果 debug 为
True
,事件循环将以调试模式运行。False
明确禁用调试模式。None
用于遵循全局 调试模式 设置。如果 loop_factory 不为
None
,它将用于创建一个新的事件循环;否则将使用asyncio.new_event_loop()
。循环在最后关闭。此函数应作为 asyncio 程序的主入口点,理想情况下只应调用一次。建议使用 loop_factory 来配置事件循环,而不是策略。执行器获得 5 分钟的超时持续时间来关闭。如果执行器在该持续时间内未完成,将发出警告并关闭执行器。
示例
async def main(): await asyncio.sleep(1) print('hello') asyncio.run(main())
3.7 版中新增。
3.9 版中更改: 更新为使用
loop.shutdown_default_executor()
。3.10 版中更改: debug 默认值为
None
,以遵循全局调试模式设置。3.12 版中更改: 添加了 loop_factory 参数。
Runner 上下文管理器¶
- class asyncio.Runner(*, debug=None, loop_factory=None)¶
一个上下文管理器,用于简化在同一上下文中调用多个异步函数。
有时,应在同一个 事件循环 和
contextvars.Context
中调用多个顶级异步函数。如果 debug 为
True
,事件循环将以调试模式运行。False
明确禁用调试模式。None
用于遵循全局 调试模式 设置。loop_factory 可用于覆盖循环创建。loop_factory 负责将创建的循环设置为当前循环。默认情况下,如果 loop_factory 为
None
,则使用asyncio.new_event_loop()
并通过asyncio.set_event_loop()
将其设置为当前事件循环。基本上,
asyncio.run()
示例可以使用 runner 使用情况重写async def main(): await asyncio.sleep(1) print('hello') with asyncio.Runner() as runner: runner.run(main())
3.11 版中新增。
- run(coro, *, context=None)¶
在嵌入式循环中运行 协程 coro。
返回协程的结果或引发其异常。
可选的关键字专用 context 参数允许为 coro 指定一个自定义
contextvars.Context
以便在其中运行。如果为None
,则使用 runner 的默认上下文。当另一个 asyncio 事件循环在同一线程中运行时,无法调用此函数。
- close()¶
关闭 runner。
完成异步生成器、关闭默认执行器、关闭事件循环并释放嵌入式
contextvars.Context
。
- get_loop()¶
返回与运行器实例关联的事件循环。
处理键盘中断¶
3.11 版中新增。
当 signal.SIGINT
由 Ctrl-C 触发时,默认情况下会在主线程中触发 KeyboardInterrupt
异常。但是,这在 asyncio
中不起作用,因为它可能会中断 asyncio 内部并导致程序无法退出。
为了缓解此问题,asyncio
如下处理 signal.SIGINT
asyncio.Runner.run()
在执行任何用户代码之前安装一个自定义signal.SIGINT
处理程序,并在退出函数时将其移除。Runner
为传递的协程创建主任务以执行。当
signal.SIGINT
由 Ctrl-C 触发时,自定义信号处理程序通过调用asyncio.Task.cancel()
取消主任务,后者在主任务中触发asyncio.CancelledError
。这会导致 Python 堆栈展开,try/except
和try/finally
块可用于资源清理。主任务取消后,asyncio.Runner.run()
触发KeyboardInterrupt
。用户可以编写一个紧密循环,该循环不能被
asyncio.Task.cancel()
中断,在这种情况下,第二个 Ctrl-C 立即触发KeyboardInterrupt
,而不会取消主任务。