執行器

原始碼: Lib/asyncio/runners.py

本節概述了用於執行 asyncio 程式碼的高階 asyncio 原語。

它們建立在事件迴圈之上,旨在簡化常見廣泛場景下的非同步程式碼使用。

執行 asyncio 程式

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

在 asyncio 事件迴圈中執行 coro 並返回結果。

該引數可以是任何 awaitable 物件。

此函式執行 awaitable,負責管理 asyncio 事件迴圈,完成非同步生成器,並關閉執行器。

當同一執行緒中執行另一個 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())

在 3.7 版本加入。

3.9 版本中變更: 更新為使用 loop.shutdown_default_executor()

3.10 版本中變更: debug 預設為 None,以遵循全域性除錯模式設定。

3.12 版本中變更: 添加了 loop_factory 引數。

3.14 版本中變更: coro 可以是任何 awaitable 物件。

備註

asyncio 策略系統已棄用,並將在 Python 3.16 中移除;從那時起,將需要顯式的 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())

在 3.11 版本中新增。

run(coro, *, context=None)

在嵌入的事件迴圈中執行 coro

該引數可以是任何 awaitable 物件。

如果引數是協程,它將被封裝在一個任務中。

一個可選的僅關鍵字 context 引數允許為要執行的程式碼指定自定義的 contextvars.Context。如果 context 為 None,則使用執行器的預設上下文。

返回 awaitable 的結果或引發異常。

當同一執行緒中執行另一個 asyncio 事件迴圈時,不能呼叫此函式。

3.14 版本中變更: coro 可以是任何 awaitable 物件。

close()

關閉執行器。

完成非同步生成器,關閉預設執行器,關閉事件迴圈並釋放嵌入的 contextvars.Context

get_loop()

返回與執行器例項關聯的事件迴圈。

備註

Runner 使用惰性初始化策略,其建構函式不初始化底層低階結構。

嵌入的 loopcontextwith 語句體進入時或首次呼叫 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,而不會取消主任務。