策略

事件循环策略是一个全局对象,用于获取和设置当前事件循环,以及创建新的事件循环。默认策略可以通过替换内置的替代方案来使用不同的事件循环实现,或者用可以覆盖这些行为的自定义策略来替换。

策略对象获取和设置每个上下文的单独事件循环。默认情况下,这是每个线程的,但自定义策略可能会以不同的方式定义上下文

自定义事件循环策略可以控制get_event_loop()set_event_loop()new_event_loop() 的行为。

策略对象应实现AbstractEventLoopPolicy抽象基类中定义的 API。

获取和设置策略

可以使用以下函数来获取和设置当前进程的策略

asyncio.get_event_loop_policy()

返回当前进程范围的策略。

asyncio.set_event_loop_policy(policy)

将当前进程范围的策略设置为 policy

如果将 policy 设置为 None,则恢复默认策略。

策略对象

抽象事件循环策略基类定义如下

class asyncio.AbstractEventLoopPolicy

asyncio 策略的抽象基类。

get_event_loop()

获取当前上下文的事件循环。

返回实现AbstractEventLoop 接口的事件循环对象。

此方法永远不应返回 None

在 3.6 版本中更改。

set_event_loop(loop)

将当前上下文的事件循环设置为 loop

new_event_loop()

创建并返回新的事件循环对象。

此方法永远不应返回 None

get_child_watcher()

获取子进程监视器对象。

返回实现 AbstractChildWatcher 接口的监视器对象。

此函数特定于 Unix。

在 3.12 版本中弃用。

set_child_watcher(watcher)

将当前子进程监视器设置为 watcher

此函数特定于 Unix。

在 3.12 版本中弃用。

asyncio 附带以下内置策略

class asyncio.DefaultEventLoopPolicy

默认的 asyncio 策略。在 Unix 上使用 SelectorEventLoop,在 Windows 上使用 ProactorEventLoop

无需手动安装默认策略。asyncio 配置为自动使用默认策略。

在 3.8 版本中更改:在 Windows 上,现在默认使用 ProactorEventLoop

在 3.12 版本中弃用:如果没有设置当前事件循环并且它决定创建一个,则默认 asyncio 策略的 get_event_loop() 方法现在会发出 DeprecationWarning。在未来的某个 Python 版本中,这将变成错误。

class asyncio.WindowsSelectorEventLoopPolicy

使用 SelectorEventLoop 事件循环实现的替代事件循环策略。

可用性:Windows。

class asyncio.WindowsProactorEventLoopPolicy

使用 ProactorEventLoop 事件循环实现的替代事件循环策略。

可用性:Windows。

进程监视器

进程监视器允许自定义事件循环如何在 Unix 上监视子进程。具体来说,事件循环需要知道子进程何时退出。

在 asyncio 中,使用 create_subprocess_exec()loop.subprocess_exec() 函数创建子进程。

asyncio 定义了 AbstractChildWatcher 抽象基类,子监视器应实现该基类,并且有四种不同的实现:ThreadedChildWatcher(配置为默认使用)、MultiLoopChildWatcherSafeChildWatcherFastChildWatcher

另请参阅 子进程和线程 部分。

以下两个函数可用于自定义 asyncio 事件循环使用的子进程监视器实现

asyncio.get_child_watcher()

返回当前策略的当前子监视器。

在 3.12 版本中弃用。

asyncio.set_child_watcher(watcher)

将当前策略的当前子监视器设置为 watcherwatcher 必须实现 AbstractChildWatcher 基类中定义的方法。

在 3.12 版本中弃用。

注意

第三方事件循环实现可能不支持自定义子监视器。对于此类事件循环,使用 set_child_watcher() 可能会被禁止或不起作用。

class asyncio.AbstractChildWatcher
add_child_handler(pid, callback, *args)

注册一个新的子进程处理程序。

安排在 PID 等于 pid 的进程终止时调用 callback(pid, returncode, *args)。 为同一进程指定另一个回调将替换先前的处理程序。

callback 可调用对象必须是线程安全的。

remove_child_handler(pid)

移除 PID 等于 pid 的进程的处理程序。

如果成功移除处理程序,则该函数返回 True,如果没有任何内容可移除,则返回 False

attach_loop(loop)

将监视器附加到事件循环。

如果监视器之前已附加到事件循环,则在附加到新循环之前,会先将其分离。

注意:loop 可以为 None

is_active()

如果监视器可以使用,则返回 True

使用 非活跃 当前子进程监视器生成子进程会引发 RuntimeError

在 3.8 版本中新增。

close()

关闭监视器。

必须调用此方法以确保清理底层资源。

在 3.12 版本中弃用。

class asyncio.ThreadedChildWatcher

此实现为每个子进程生成启动一个新的等待线程。

即使 asyncio 事件循环在非主操作系统线程中运行,它也能可靠地工作。

处理大量子进程时没有明显的开销(每次子进程终止时为 O(1)),但是每个进程启动一个线程需要额外的内存。

此监视器默认使用。

在 3.8 版本中新增。

class asyncio.MultiLoopChildWatcher

此实现会在实例化时注册一个 SIGCHLD 信号处理程序。 这可能会破坏为 SIGCHLD 信号安装自定义处理程序的第三方代码。

监视器通过在 SIGCHLD 信号上显式轮询每个进程来避免干扰其他生成进程的代码。

一旦安装了监视器,从不同线程运行子进程就没有限制。

该解决方案是安全的,但是在处理大量进程时会产生明显的开销(每次接收到 SIGCHLD 时为 O(n))。

在 3.8 版本中新增。

在 3.12 版本中弃用。

class asyncio.SafeChildWatcher

此实现使用主线程中的活动事件循环来处理 SIGCHLD 信号。如果主线程没有运行的事件循环,则另一个线程无法生成子进程(会引发 RuntimeError)。

监视器通过在 SIGCHLD 信号上显式轮询每个进程来避免干扰其他生成进程的代码。

此解决方案与 MultiLoopChildWatcher 一样安全,并且具有相同的 O(n) 复杂度,但是需要在主线程中运行事件循环才能工作。

在 3.12 版本中弃用。

class asyncio.FastChildWatcher

此实现通过直接调用 os.waitpid(-1) 来回收每个终止的进程,可能会破坏其他生成进程并等待其终止的代码。

处理大量子进程时没有明显的开销(每次子进程终止时为 O(1))。

此解决方案需要在主线程中运行事件循环才能工作,如 SafeChildWatcher

在 3.12 版本中弃用。

class asyncio.PidfdChildWatcher

此实现轮询进程文件描述符 (pidfds) 以等待子进程终止。 在某些方面,PidfdChildWatcher 是一个“金发姑娘”子进程监视器实现。 它不需要信号或线程,不会干扰事件循环之外启动的任何进程,并且随着事件循环启动的子进程数量线性扩展。 主要缺点是 pidfds 是 Linux 特有的,并且仅在最新的 (5.3+) 内核上工作。

在 3.9 版本中新增。

自定义策略

要实现新的事件循环策略,建议对 DefaultEventLoopPolicy 进行子类化,并覆盖需要自定义行为的方法,例如:

class MyEventLoopPolicy(asyncio.DefaultEventLoopPolicy):

    def get_event_loop(self):
        """Get the event loop.

        This may be None or an instance of EventLoop.
        """
        loop = super().get_event_loop()
        # Do something with loop ...
        return loop

asyncio.set_event_loop_policy(MyEventLoopPolicy())