# 策略

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

策略对象 可为每个 context 获取和设置单独的事件循环。 在默认情况下是分线程，不过自定义策略可以按不同的方式定义 context。

自定义事件循环策略可以控制 `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

  异步策略的抽象基类。

  get_event_loop()

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

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

    该方法永远不应返回 `None`。

    在 3.6 版本发生变更.

  set_event_loop(loop)

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

  new_event_loop()

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

    该方法永远不应返回 `None`。

  get_child_watcher()

    获取子进程监视器对象。

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

    该函数仅支持Unix。

  set_child_watcher(watcher)

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

    该函数仅支持Unix。

asyncio附带下列内置策略:

class asyncio.DefaultEventLoopPolicy

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

  不需要手动安装默认策略。asyncio已配置成自动使用默认策略。

  在 3.8 版本发生变更: 在 Windows 上，现在默认会使用 `ProactorEventLoop`。

  备注

  在 Python 3.10.9, 3.11.1 和 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` (已配置为默认使用), `MultiLoopChildWatcher`, `SafeChildWatcher` 和 `FastChildWatcher`。

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

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

asyncio.get_child_watcher()

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

asyncio.set_child_watcher(watcher)

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

备注

第三方事件循环实现可能不支持自定义子监视器。对于这样的事件循环，禁止使用 `set_child_watcher()` 或不起作用。

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

    注册一个新的子处理回调函数。

    安排 `callback(pid, returncode, *args)` 在进程的PID与 pid 相等时调用。指定另一个同进程的回调函数替换之前的回调处理函数。

    回调函数 callback 必须是线程安全。

  remove_child_handler(pid)

    删除进程PID与 pid 相等的进程的处理函数。

    处理函数成功删除时返回 `True` ，没有删除时返回 `False` 。

  attach_loop(loop)

    给一个事件循环绑定监视器。

    如果监视器之前已绑定另一个事件循环，那么在绑定新循环前会先解绑原来的事件循环。

    注意：循环有可能是 `None` 。

  is_active()

    如果监视器已准备好使用则返回 `True`。

    使用 不活动的 当前子监视器生成子进程将引发 `RuntimeError`。

    在 3.8 版本加入.

  close()

    关闭监视器。

    必须调用这个方法以确保相关资源会被清理。

class asyncio.ThreadedChildWatcher

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

  即使是当 asyncio 事件循环运行在非主 OS 线程上时它也能可靠地工作。

  当处理大量子进程时不存在显著的开销 (每次子进程结束时为 O(1))，但当每个进程启动一个线程时则需要额外的内存。

  此监视器会默认被使用。

  在 3.8 版本加入.

class asyncio.MultiLoopChildWatcher

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

  此监视器会在收到 `SIGCHLD` 信号时通过显式地轮询每个进程来避免干扰其他代码生成的进程。

  该监视器一旦被安装就不会限制从不同线程运行子进程。

  该解决方案是安全的，但在处理大量进程时会有显著的开销 (每收到一个 `SIGCHLD` 时为 O(n))。

  在 3.8 版本加入.

class asyncio.SafeChildWatcher

  该实现会使用主线程中的活动事件循环来处理 `SIGCHLD` 信号。 如果主线程没有正在运行的事件循环，则其他线程无法生成子进程 (会引发 `RuntimeError`)。

  此监视器会在收到 `SIGCHLD` 信号时通过显式地轮询每个进程来避免干扰其他代码生成的进程。

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

class asyncio.FastChildWatcher

  这种实现直接调用 `os.waitpid(-1)` 来获取所有已结束的进程，可能会中断其它代码洐生进程并等待它们结束。

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

  该解决方案需要主线程有正在运行的事件循环才能工作，这与 `SafeChildWatcher` 一样。

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())