运行器¶
本节概述了用于运行 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* 来配置事件循环,而不是策略。传递asyncio.EventLoop
允许在没有策略系统的情况下运行 asyncio。执行器被赋予 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* 参数。
运行器上下文管理器¶
- 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()
示例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
,则使用运行器的默认上下文。当同一线程中运行另一个 asyncio 事件循环时,无法调用此函数。
- close()¶
关闭运行器。
最终化异步生成器、关闭默认执行器、关闭事件循环并释放嵌入式
contextvars.Context
。
- get_loop()¶
返回与运行器实例关联的事件循环。
注意
Runner
使用延迟初始化策略,其构造函数不初始化底层低级结构。嵌入式 *loop* 和 *context* 在进入
with
主体或首次调用run()
或get_loop()
时创建。
处理键盘中断¶
3.11 版本新增。
当 Ctrl-C 引发 signal.SIGINT
时,默认情况下会在主线程中引发 KeyboardInterrupt
异常。但是,这不适用于 asyncio
,因为它会中断 asyncio 内部,并可能导致程序挂起而无法退出。
为了缓解此问题,asyncio
按以下方式处理 signal.SIGINT
asyncio.Runner.run()
在任何用户代码执行之前安装一个自定义的signal.SIGINT
处理程序,并在函数退出时将其移除。Runner
为传递的协程创建主任务以执行。当通过 Ctrl-C 触发
signal.SIGINT
时,自定义信号处理程序会通过调用asyncio.Task.cancel()
来取消主任务,这会在主任务内部引发asyncio.CancelledError
。这会导致 Python 堆栈展开,可以使用try/except
和try/finally
代码块进行资源清理。在主任务被取消后,asyncio.Runner.run()
会引发KeyboardInterrupt
。用户可能会编写一个无法被
asyncio.Task.cancel()
中断的紧密循环,在这种情况下,第二次按下 Ctrl-C 会立即引发KeyboardInterrupt
,而不会取消主任务。