Runners (執行器)

原始碼:Lib/asyncio/runners.py

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

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

運行一個 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.

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

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

If loop_factory is not None, it is used to create a new event loop; otherwise asyncio.new_event_loop() is used. The loop is closed at the end. This function should be used as a main entry point for asyncio programs, and should ideally only be called once. It is recommended to use loop_factory to configure the event loop instead of policies. Passing asyncio.EventLoop allows running asyncio without the policy system.

The executor is given a timeout duration of 5 minutes to shutdown. If the executor hasn't finished within that duration, a warning is emitted and the executor is closed.

範例:

async def main():
    await asyncio.sleep(1)
    print('hello')

asyncio.run(main())

在 3.7 版被加入.

在 3.9 版的變更: Updated to use loop.shutdown_default_executor().

在 3.10 版的變更: debug is None by default to respect the global debug mode settings.

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

在 3.14 版的變更: coro can be any awaitable object.

Runner context manager

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

A context manager that simplifies multiple async function calls in the same context.

Sometimes several top-level async functions should be called in the same event loop and contextvars.Context.

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

loop_factory could be used for overriding the loop creation. It is the responsibility of the loop_factory to set the created loop as the current one. By default asyncio.new_event_loop() is used and set as current event loop with asyncio.set_event_loop() if loop_factory is None.

Basically, asyncio.run() example can be rewritten with the runner usage:

async def main():
    await asyncio.sleep(1)
    print('hello')

with asyncio.Runner() as runner:
    runner.run(main())

在 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.

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

在 3.14 版的變更: coro can be any awaitable object.

close()

Close the runner.

Finalize asynchronous generators, shutdown default executor, close the event loop and release embedded contextvars.Context.

get_loop()

Return the event loop associated with the runner instance.

備註

Runner uses the lazy initialization strategy, its constructor doesn't initialize underlying low-level structures.

Embedded loop and context are created at the with body entering or the first call of run() or get_loop().

Handling Keyboard Interruption

在 3.11 版被加入.

When signal.SIGINT is raised by Ctrl-C, KeyboardInterrupt exception is raised in the main thread by default. However this doesn't work with asyncio because it can interrupt asyncio internals and can hang the program from exiting.

To mitigate this issue, asyncio handles signal.SIGINT as follows:

  1. asyncio.Runner.run() installs a custom signal.SIGINT handler before any user code is executed and removes it when exiting from the function.

  2. The Runner creates the main task for the passed coroutine for its execution.

  3. When signal.SIGINT is raised by Ctrl-C, the custom signal handler cancels the main task by calling asyncio.Task.cancel() which raises asyncio.CancelledError inside the main task. This causes the Python stack to unwind, try/except and try/finally blocks can be used for resource cleanup. After the main task is cancelled, asyncio.Runner.run() raises KeyboardInterrupt.

  4. A user could write a tight loop which cannot be interrupted by asyncio.Task.cancel(), in which case the second following Ctrl-C immediately raises the KeyboardInterrupt without cancelling the main task.