# 18.5.2. 事件循环

源码: Lib/asyncio/events.py

## 18.5.2.1. 事件循环函数

下面的函数是访问全局策略方法的便利快捷方式。注意这只是提供了对默认策略的访问，除非要在进程开始之前设置替代的策略。

`asyncio.``get_event_loop`()

  与调用 `get_event_loop_policy().get_event_loop()` 等价。

`asyncio.``set_event_loop`(loop)

  等价于调用 `get_event_loop_policy().set_event_loop(loop)`。

`asyncio.``new_event_loop`()

  等价于调用 `get_event_loop_policy().new_event_loop()`。

## 18.5.2.2. 可用的事件循环

asyncio当前提供两种事件循环实现： `SelectorEventLoop` 和 `ProactorEventLoop` 。

class `asyncio.``SelectorEventLoop`

  基于 `selectors` 模块的事件循环。 `AbstractEventLoop` 的子类。

  使用对应平台上最高效的选择器。

  在Windows上，只支持套接字（举例：不支持管线）：参见 MSDN关于select的文档 <https://msdn.microsoft.com/en-us/library/windows/desktop/ms740141%28v=vs.85%29.aspx> _ 。

class `asyncio.``ProactorEventLoop`

  Windows上的 Proactor 事件循环，使用的是 “I/O 完成端口”，也叫做 IOCP。为 `AbstractEventLoop` 的子类。

  可用性：Windows。

  参见

  MSDN上关于 I/O完成端口的文档 。

在Windows上使用 `ProactorEventLoop` 的例子:

    import asyncio, sys

    if sys.platform == 'win32':
        loop = asyncio.ProactorEventLoop()
        asyncio.set_event_loop(loop)

## 18.5.2.3. 平台支持

`asyncio` 模块被设计为可移植的，但是每个平台仍旧有细微的不同，而且不一定支持 `asyncio` 的所有特性。

### 18.5.2.3.1. Windows

Windows事件循环的一般限制：

- 不支持 `create_unix_connection()` 和 `create_unix_server()`：`socket.AF_UNIX` 套接字族只在UNIX上可用

- 不支持 `add_signal_handler()` 和 `remove_signal_handler()`

- 不支持 `EventLoopPolicy.set_child_watcher()` 。`ProactorEventLoop` 支持子进程。监控子进程的实现只有一种，不需要对其进行设置。

特定于 `SelectorEventLoop` 的限制：

- `SelectSelector` 只被用于支持套接字，并且最多支持512个套接字。

- `add_reader()` 和 `add_writer()` 只接受套接字的文件描述符

- 不支持管线（示例：`connect_read_pipe()`, `connect_write_pipe()`）

- 不支持 Subprocesses （示例: `subprocess_exec()`, `subprocess_shell()`）

特定于 `ProactorEventLoop` 的限制：

- 不支持 `create_datagram_endpoint()` (UDP)

- 不支持 `add_reader()` 和 `add_writer()`

Windows上单调时钟的分辨率大约为 15.6 毫秒。最佳的分辨率是 0.5 毫秒。分辨率依赖于具体的硬件（HPET 的可用性）和Windows的设置。参见:ref:asyncio delayed calls <asyncio-delayed-calls> 。

在 3.5 版更改: `ProactorEventLoop` 现在支持SSL了。

### 18.5.2.3.2. Mac OS X

类似于PTY的字符设备在Mavericks （Mac OS 10.9）之前并未有很好的支持。在 Mac OS 10.5以及更老的版本上则根本不支持。

在Mac OS 10.6、10.7和10.8上，默认的事件循环是 `SelectorEventLoop` ，其使用了 `selectors.KqueueSelector` 。 `selectors.KqueueSelector` 在这些版本上并不支持字符设备。`SelectorEventLoop` 可以同 `SelectSelector` 或者 `PollSelector` 一同使用来在这些版本的Mac OS X上支持块设备。 例如:

    import asyncio
    import selectors

    selector = selectors.SelectSelector()
    loop = asyncio.SelectorEventLoop(selector)
    asyncio.set_event_loop(loop)

## 18.5.2.4. 事件循环策略和默认策略

使用一个 策略 模式对事件循环进行管理，用以为特定平台和框架提供最大限度的灵活性。在一个进程的执行中，唯一的全局策略对象，基于调用的上下文，管理着此进程所有可用的事件循环。一个策略就是一个实现了 `AbstractEventLoopPolicy` 的对象。

对于 `asyncio` 的多数使用者来说，从来都不需要显式地处理策略，因为默认的全局策略就已经足够了（看下面）。

模块级别的函数 `get_event_loop()` 和 `set_event_loop()` 对于默认策略管理的事件循环的便利访问方式。

## 18.5.2.5. 事件循环策略接口

一个事件循环必须实现如下的接口：

class `asyncio.``AbstractEventLoopPolicy`

  事件循环策略。

  `get_event_loop`()

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

    返回一个实现了 `AbstractEventLoop` 接口的事件循环对象。如果是从一个协程中调用，就会返回当前正在运行的事件循环。

    如果没有为当前上下文设置一个事件循环，并且当前策略不能创建一个事件循环，就会抛出一个错误。其务必不能返回 `None` 。

    在 3.6 版更改.

  `set_event_loop`(loop)

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

  `new_event_loop`()

    根据策略的规则，创建并返回一个新的事件循环对象。

    如果有需要设置这个循环作为当前上下文的事件循环，就必须显式调用 `set_event_loop()` 。

The default policy defines context as the current thread, and manages an event loop per thread that interacts with `asyncio`. If the current thread doesn’t already have an event loop associated with it, the default policy’s `get_event_loop()` method creates one when called from the main thread, but raises `RuntimeError` otherwise.

## 18.5.2.6. 访问全局循环策略

`asyncio.``get_event_loop_policy`()

  获取当前事件循环策略。

`asyncio.``set_event_loop_policy`(policy)

  设置当前事件循环策略。 如果 policy 为 `None` ，就会恢复使用默认的策略。

## 18.5.2.7. 自定义事件循环策略

要实现一个新的事件循环策略，推荐你继承具体的默认事件循环策略 `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())