事件循环实现:libuv和libev#

在 1.3 版本加入.

gevent提供了两个事件循环库的选择 (libevlibuv )以及三个事件循环实现。本文档将探讨这些实现,并将它们相互比较。

使用非默认循环#

首先,我们将描述如何选择一个事件循环,而不是给定平台的默认循环。这是通过设置 GEVENT_LOOP 启动程序前的环境变量,或通过设置 gevent.config.loop 在代码中。

重要

如果选择在python代码中配置循环,则必须完成 立即 在导入gevent之后,在完成任何其他gevent导入或异步操作之前,最好是在程序的顶部,在monkey补丁的正上方(如果完成)::

import gevent
gevent.config.loop = "libuv"

重要

在gevent 1.4和1.3中,如果从分布在pypi上的manylinux1二进制机轮安装gevent,将无法使用libuv循环。您需要从源代码处编译以获得对libuv的访问权。GEvent 1.5分配有libuv支持的manylinx2010车轮。

如果您使用的是Linux发行版的gevent包,那么除了默认值之外,您可能有也可能没有其他任何循环。

循环实现#

这里我们将描述可用的循环实现。

名字

类库

违约

口译员

年龄

实施

生成状态

嵌入的

小精灵

小精灵

非Windows平台上的cpython

仅限CPython

8年

赛隆

违约

默认;可选

利贝夫CFFI

小精灵

非Windows平台上的Pypy

塞顿和派比

4年

CFFI

可选;默认

默认;可选

小精灵

小精灵

Windows上的所有解释程序

塞顿和派比

2年

CFFI

可选;默认

默认;可选

小精灵#

libev 是一个值得尊敬的事件循环库,自2011年1.0A1取代libevent以来,它一直是gevent中的默认库。libev从2007年就存在了。

备注

在将来,这个赛通实现可能会被弃用,取而代之的是 利贝夫CFFI .

开发和来源

libev是一个稳定的库,不会很快改变。更改通过电子邮件发送到邮件列表的修补程序的形式被接受。由于它的时代和可移植性要求,它大量使用预处理器宏,有些人可能会发现这妨碍了源代码的可读性。

平台支撑

gevent在Linux和MacOS上测试libev。目前还没有libev官方支持的平台列表,尽管有报道称freebsd、openbsd和solaris/smartos在libev上的各个方面都与gevent合作。

在Windows上,libev many limitations . gevent依赖于Microsoft C运行时函数,使用稍微复杂的映射从Windows套接字句柄映射到libev的整数文件描述符;这会阻止使用cffi实现(反过来又阻止pypypy在Windows上使用libev)。

libev本身没有已知的公共CI基础设施。

利贝夫CFFI#

它使用的libev和上面的完全一样,但不是cython,而是cffi。这使得它适合Pypy(和默认值)。它还可以使调试更容易,因为为跟踪保留了更多的细节。

备注

在未来,这种CFFI实施可能会成为默认和替代 小精灵 .

何时使用

在Pypy上或调试时。

小精灵#

libuv是自2011年以来为使用节点0.5开发的事件循环库。它最初是在非Windows平台上围绕libev的包装,直接在Windows上使用本机Windows IOCP支持(此代码由Microsoft提供)。现在,它在所有支持的平台上都有自己的循环实现。

libuv提供libev "poll handles" 在gevent 1.3中,这就是gevent对io的使用。但libuv还提供了更高级别的读写请求抽象,这些抽象可能会提高性能。将来,gevent可能会使用这些抽象。

备注

将来,此实现可能成为所有平台上的默认实现。

开发和来源

libuv由libuv组织于 github . 它有一个大型的、活跃的社区,并在许多流行的项目中使用,包括node.js。

源代码是以一种干净一致的编码风格编写的,这可能使读取和调试更加容易。

平台支撑

gevent在Linux、Windows和MacOS上测试libuv。libuv发布了 supported platforms 这很可能与Gevent合作。小精灵 maintains a public CI infrastructure .

何时使用libuv

  • 你想在Windows上使用pypy。

  • 您希望在Windows上开发(不建议在生产环境中使用Windows)。

  • 您希望使用libev不支持的操作系统,如ibm i。

    备注

    除Linux、MacOS和Windows以外的平台未经Gevent测试。

限制和差异#

由于其新颖性,以及类库和生态系统固有的一些设计决策,与libev相比,gevent使用libuv的方式存在一些局限性和差异。

  • 计时器(例如 gevent.sleepgevent.Timeout )只支持1毫秒的分辨率(实际上接近1.5毫秒)。尝试使用较小的物体会自动将其增加到1毫秒并发出警告。因为libuv只支持毫秒分辨率,将更高精度的时钟舍入到整数毫秒,所以计时器显然会受到更大的抖动。

  • 使用负超时的行为可能与libev不同。

  • libuv阻止了所有信号的传输,因此使用(任意)0.3秒计时器处理信号。这意味着信号处理将被延迟到该数量,并且事件循环可以在操作系统的 poll 打电话就是这个数目。注意,这也是Gevent在Windows上为libev所做的。

  • libuv每个文件描述符只支持一个IO观察程序,而libev和gevent始终支持使用不同设置的许多观察程序。libev行为在python级别被模拟。

  • 在不读取或写入任何数据的情况下(与libev一起使用),循环多次并期望每次引发相同文件描述符的事件在使用Linux时似乎无法正常工作 gevent.select.poll 或者一只猴子 selectors.PollSelector .

  • 如果有什么意外的事情发生,Libuv喜欢 abort() 整个过程而不是报告错误。例如,关闭监视程序中使用的文件描述符可能会导致整个进程退出。

  • 调用计时器和其他回调的顺序可能不同于libev。特别是,计时器和IO回调以不同的顺序发生,计时器可能很容易被关闭,最多达到标称1毫秒分辨率的一半。见 issue #1057 .

  • 在观察者类中不支持优先级。libev对优先级有一些支持,这在低级别的gevent api中公开了,但从未记录在案。

  • 低水平 forkchild 观察者不可用。Gevent在提供 os.fork() . 儿童观察者使用 SIGCHLD 就像Libev一样,同样的警告也适用。

  • 低水平 prepare 观察者不可用。GEvent使用Prepare Watchers进行内部目的。如果需要,可以在Python中模拟。

性能#

在各种微基准GEvent中,三个循环实现的性能大致相同。似乎没有明显的赢家或输家。