事件循环实现:libuv和libev#
在 1.3 版本加入.
gevent提供了两个事件循环库的选择 (libev 和 libuv )以及三个事件循环实现。本文档将探讨这些实现,并将它们相互比较。
使用非默认循环#
首先,我们将描述如何选择一个事件循环,而不是给定平台的默认循环。这是通过设置 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.sleep
和gevent.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中公开了,但从未记录在案。
低水平
fork
和child
观察者不可用。Gevent在提供os.fork()
. 儿童观察者使用SIGCHLD
就像Libev一样,同样的警告也适用。低水平
prepare
观察者不可用。GEvent使用Prepare Watchers进行内部目的。如果需要,可以在Python中模拟。
性能#
在各种微基准GEvent中,三个循环实现的性能大致相同。似乎没有明显的赢家或输家。