运行器

源代码: Lib/asyncio/runners.py

本节将简述用于运行异步代码的高层级异步原语。

它们构建于 事件循环 之上,其目标是简化针对常见通用场景的异步代码的用法。

运行 asyncio 程序

asyncio.run(coro, *, debug=None, loop_factory=None)

Execute coro in an asyncio event loop and return the result.

The argument can be any awaitable object.

This function runs the awaitable, taking care of managing the asyncio event loop, finalizing asynchronous generators, and closing the executor.

当有其他 asyncio 事件循环在同一线程中运行时,此函数不能被调用。

如果 debugTrue,事件循环将运行于调试模式。 False 将显式地禁用调试模式。 使用 None 将沿用全局 Debug 模式 设置。

如果 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())

Added in version 3.7.

在 3.9 版本发生变更: 更新为使用 loop.shutdown_default_executor()

在 3.10 版本发生变更: 默认情况下 debugNone 即沿用全局调试模式设置。

在 3.12 版本发生变更: 增加了 loop_factory 形参。

在 3.14 版本发生变更: coro can be any awaitable object.

运行器上下文管理器

class asyncio.Runner(*, debug=None, loop_factory=None)

对在相同上下文中 多个 异步函数调用进行简化的上下文管理器。

有时多个最高层级异步函数应当在同一个 事件循环contextvars.Context 中被调用。

如果 debugTrue,事件循环将运行于调试模式。 False 将显式地禁用调试模式。 使用 None 将沿用全局 Debug 模式 设置。

loop_factory 可被用来重载循环的创建。 loop_factory 要负责将所创建的循环设置为当前事件循环。 在默认情况下如果 loop_factoryNone 则会使用 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())

Added in version 3.11.

run(coro, *, context=None)

Execute coro in the embedded event loop.

The argument can be any awaitable object.

If the argument is a coroutine, it is wrapped in a Task.

An optional keyword-only context argument allows specifying a custom contextvars.Context for the code to run in. The runner's default context is used if context is None.

Returns the awaitable's result or raises an exception.

当有其他 asyncio 事件循环在同一线程中运行时,此函数不能被调用。

在 3.14 版本发生变更: coro can be any awaitable object.

close()

关闭运行器。

最终化异步生成器,停止默认执行器,关闭事件循环并释放嵌入的 contextvars.Context

get_loop()

返回关联到运行器实例的事件循环。

备注

Runner 会使用惰性初始化策略,它的构造器不会初始化下层的低层级结构体。

嵌入的 loopcontext 是在进入 with 语句体或者对 run()get_loop() 的首次调用时被创建的。

处理键盘中断

Added in version 3.11.

signal.SIGINTCtrl-C 引发时,默认将在主线程中引发 KeyboardInterrupt。 但是这并不适用于 asyncio 因为它可以中断异步的内部操作并能挂起要退出的程序。

为解决此问题,asyncio 将按以下步骤处理 signal.SIGINT:

  1. asyncio.Runner.run() 在任何用户代码被执行之前安装一个自定义的 signal.SIGINT 处理器并在从该函数退出时将其移除。

  2. Runner 为所传入的协程创建主任务供其执行。creates the main task for the passed coroutine for its execution.

  3. signal.SIGINTCtrl-C 引发时,自定义的信号处理器将通过调用 asyncio.Task.cancel() 在主任务内部引发 asyncio.CancelledError 来取消主任务。 这将导致 Python 栈回退,try/excepttry/finally 代码块可被用于资源清理。 在主任务被取消之后,asyncio.Runner.run() 将引发 KeyboardInterrupt

  4. 用户可以编写无法通过 asyncio.Task.cancel() 来中断的紧密循环,在这种情况下后续的第二次 Ctrl-C 将立即引发 KeyboardInterrupt 而不会取消主任务。