Runner¶
這個章節概述用於執行 asyncio 程式碼的高階 asyncio 原語。
他們是基於一個事件迴圈,目的是為了簡化常見且廣泛運用場景的非同步程式碼。
執行 asyncio 程式¶
- asyncio.run(coro, *, debug=None, loop_factory=None)¶
在 asyncio 事件迴圈中執行 coro 並回傳結果。
該引數可以是任何可等待物件 (awaitable object)。
這個函式負責執行傳入的可等待物件、管理 asyncio 的事件迴圈、最終化非同步產生器以及關閉執行器。
當另一個非同步事件迴圈在同一執行緒中執行時,無法呼叫此函式。
如果 debug 為
True,事件迴圈會以除錯模式執行。False則會關閉除錯模式。None則會優先使用除錯模式的全域設定。如果 loop_factory 不為
None,它會被用於建立一個新的事件迴圈;否則會改用asyncio.new_event_loop()。迴圈會在最後關閉。這個函式應該要作為asyncio 程式的主要進入點,且理想上僅會被呼叫一次。推薦使用 loop_factory 來設定事件迴圈時而不是使用 policies(政策)。傳遞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 可以是任何可等待物件。
備註
asyncio的政策系統已被棄用,並將於 Python 3.16 移除;此後必須明確指定 loop_factory 才能設定事件迴圈。
Runner 情境管理器¶
- 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()的範例可以改寫為使用 Runner: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 object)。
如果引數是協程,會將其包裝在 Task 中。
可選的僅限關鍵字引數 context,可用來指定程式碼執行時所使用的自訂
contextvars.Context。如果 context 為None,則使用 Runner 的預設情境。回傳可等待物件的結果,或引發例外。
當另一個非同步事件迴圈在同一執行緒中執行時,無法呼叫此函式。
在 3.14 版的變更: coro 可以是任何可等待物件。
- close()¶
關閉 Runner。
最終化非同步產生器、關閉預設執行器與事件迴圈,並釋放內嵌的
contextvars.Context。
- get_loop()¶
回傳與 Runner 實例關聯的事件迴圈。
備註
Runner採用延遲初始化策略,其建構函式不會初始化底層的低階結構。內嵌的 loop 和 context 會在進入
with陳述式主體,或第一次呼叫run()或get_loop()時建立。
處理鍵盤中斷¶
在 3.11 版被加入.
當 signal.SIGINT 由 Ctrl-C 引發時,預設會在主執行緒中引發 KeyboardInterrupt 例外。然而,此方式不適用於 asyncio,因為這可能會中斷 asyncio 內部運作,並造成程式無法結束。
為了緩解此問題,asyncio 會依下列方式處理 signal.SIGINT:
asyncio.Runner.run()會在執行任何使用者程式碼之前安裝自訂的signal.SIGINT處理程式,並在函式結束時將其移除。Runner會為傳入的協程建立主 Task,以執行該協程。當
signal.SIGINT由 Ctrl-C 引發時,自訂訊號處理程式會呼叫asyncio.Task.cancel()來取消主 Task,而此方法會在主 Task 內引發asyncio.CancelledError。這會使 Python 堆疊展開,並可使用try/except與try/finally區塊來清理資源。主 Task 取消後,asyncio.Runner.run()會引發KeyboardInterrupt。使用者可能會撰寫一個無法由
asyncio.Task.cancel()中斷的緊密迴圈;在此情況下,再次按下 Ctrl-C 會立即引發KeyboardInterrupt,而不會取消主 Task。