執行器

原始碼: Lib/asyncio/runners.py

本節概述了用於執行 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

  1. asyncio.Runner.run() 在任何使用者程式碼執行之前安裝一個自定義的 signal.SIGINT 處理程式,並在函式退出時將其移除。

  2. Runner 為傳遞的協程建立主任務以執行。

  3. 當透過 Ctrl-C 觸發 signal.SIGINT 時,自定義訊號處理程式會透過呼叫 asyncio.Task.cancel() 來取消主任務,這會在主任務內部引發 asyncio.CancelledError。這會導致 Python 堆疊展開,可以使用 try/excepttry/finally 程式碼塊進行資源清理。在主任務被取消後,asyncio.Runner.run() 會引發 KeyboardInterrupt

  4. 使用者可能會編寫一個無法被 asyncio.Task.cancel() 中斷的緊密迴圈,在這種情況下,第二次按下 Ctrl-C 會立即引發 KeyboardInterrupt,而不會取消主任務。