Runners (執行器)

原始碼:Lib/asyncio/runners.py

這個章節概述用於執行 asyncio 程式碼的高階 asyncio 原始物件。

他們是基於一個事件迴圈,目的是為了簡化常見且廣泛運用場景的非同步程式碼。

運行一個 asyncio 程式

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

執行協程 (coroutine) coro 並回傳結果。

這個函式負責運行被傳入的協程、管理 asyncio 的事件迴圈、終結非同步產生器,以及關閉執行器。

當另一個非同步事件迴圈在同一執行緒中執行時,無法呼叫此函式。

如果 debugTrue,事件迴圈會以除錯模式執行。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())

Added in version 3.7.

在 3.9 版的變更: 更新为使用 loop.shutdown_default_executor()

在 3.10 版的變更: 默认情况下 debugNone 即沿用全局调试模式设置。

在 3.12 版的變更: 新增 loop_factory 參數。

运行器上下文管理器

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

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

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

如果 debugTrue,事件迴圈會以除錯模式執行。False 則會關閉除錯模式。None 則會優先使用除錯模式的全域設定。

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)

在嵌入的循环中运行一个 协程 coro

返回协程的结果或者引发其异常。

可选的仅限关键字参数 context 允许指定一个自定义 contextvars.Context 用作 coro 运行所在的上下文。 如果为 None 则会使用运行器的默认上下文。

當另一個非同步事件迴圈在同一執行緒中執行時,無法呼叫此函式。

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 而不会取消主任务。