Asyncio 与 Gevent [关闭]

Posted

技术标签:

【中文标题】Asyncio 与 Gevent [关闭]【英文标题】:Asyncio vs. Gevent [closed] 【发布时间】:2019-06-12 17:38:13 【问题描述】:

背景

我曾经在一个 Python2 系统上工作过,该系统有很多同步编写的自定义 I/O 代码,并使用线程进行扩展。在某些时候,我们无法进一步扩展它,并意识到我们必须切换到异步编程。

Twisted 是流行的选择,但我们想避免它的回调地狱。 它确实有 @inlineCallbacks 装饰器,它使用生成器魔法有效地实现了协程,就像其他一些库一样。这更可以忍受,但感觉有点片状。 然后我们找到了gevent。您所要做的就是:
from gevent import monkey
monkey.patch_all()

就这样,您所有的标准 I/O(套接字、数据库事务,实际上是用纯 Python 编写的所有内容)都是异步的,使用 greenlets 在幕后产生和切换。

这并不完美:

当时,它在 Windows 上运行不佳(今天仍然存在一些限制)。幸运的是,我们在 Linux 上运行。 它不能猴子补丁 C 扩展,所以我们不能使用 mysqldb,例如。幸运的是,有许多纯 Python 替代品,例如 PyMySQL。

问题

如今,Python 3 变得更加流行,随之而来的是 - asyncio。就个人而言,我认为这很棒,但最近有人问我,它与我们用 gevent 实现的有什么不同,并且无法给出足够好的答案。

这听起来可能很主观,但我实际上是在寻找真正的用例,其中一个会显着优于另一个,或者允许另一个不允许的东西。以下是我迄今为止收集的注意事项:

    就像我说的,gevent 在 Windows 上相当有限。再说一次,我所知道的大多数生产代码都在 Linux 上运行。

    如果您需要在 Windows 上运行,请使用 asyncio

    Gevent 不能对 C 扩展进行猴子补丁。但是,asyncio 不能对 任何东西进行猴子补丁。

    想象一下出现了一种新的数据库技术,并且您想使用它,但是没有一个纯 Python 库来支持它,因此您无法将它与 Gevent 集成。问题是,当没有可以与 asyncio 集成的 io* 库时,您同样会陷入困境!当然,还有工作线程和执行程序,但这不是重点,无论如何,在这两种情况下都可以正常工作。

    有人说这是个人喜好问题,但我认为可以公平地说同步编程本质上比异步编程更容易(想想看:你有没有遇到过一个可以使用套接字的新手程序员,但有一个很难理解如何正确选择/轮询它们,或者思考未来/承诺?你有没有遇到过相反的情况?)。

    无论如何,我们不要去那里。我想解决这一点,因为它经常出现(here's reddit 上的讨论),但我真正追求的是您有实际理由使用其中一个或另一个的场景。

    Asyncio 是标准库的一部分。这是巨大的:这意味着它得到了很好的维护,有据可查,每个人都知道它并默认使用它。

    但是,考虑到您需要了解多少 Gevent 才能使用它(而且它的维护和文档也非常好),它似乎并不那么重要。因此,即使对于涉及期货的最复杂场景,*** 也有多种答案,但完全不使用期货的可能性似乎同样可行。

Guido 和 Python 社区当然有充分的理由在 Asyncio 上投入如此多的精力,甚至在语言中引入新的关键字 - 我似乎找不到它们。

两者之间的主要区别是什么?在什么情况下会变得明显?

【问题讨论】:

我不相信这是客观的,但我不会以“主要观点”投票结束。不过,我建议您可以减少 很多 的闲聊内容,以使其更加客观。 例如:“当然,即使是涉及期货的最复杂的场景,在 *** 上有多个答案也很好——但完全不必使用期货更好。”这怎么不是主观的? 感谢理解!我可以放心地说“异步编程比同步编程更难”或“futures 是一项高级功能,不必使用它很好”的原因实际上是由一个关于元类的具体用途的问题所鼓励的(***.com/questions/392160/… )。与 asyncio 不同,它是 Python 中的一个标准且有用的特性——但相对公平地说,它比讨论的替代方案更先进。 呃,好的。你已经清楚地给出了一个非常详细的答案,但自从提出这个问题以来,SO 的标准也发生了很大的变化。仍然只是我的意见,但我担心除非你削减它,否则你会在这方面面临密切的投票;当然,是否要更改文本由其他人和您决定。不过,无论如何,我无法给你一个合适的答案。 感谢您的坦率。我会尽量减少它。 【参考方案1】:

来自实际使用的“简单”答案:

    gevent 的好处——你可以修补东西,这意味着你[理论上]可以使用同步库。 IE。你可以给 django 打补丁。 gevent 的坏处 — 并非所有东西都可以修补,如果您必须使用一些无法修补的 DB 驱动程序,那你就完蛋了 gevent 最糟糕的事情——它是“神奇的”。理解“patch_all”会发生什么需要付出巨大的努力,同样的努力也适用于为您的开发团队寻找/雇用新人。更糟糕的是——调试基于 gevent 的代码简直就是地狱。我想说,和回调差不多,如果不是更糟的话。

我认为后一点是关键。软件工程中最被低估的事情是代码的目的是读取,而不是编写或有效运行(如果以后是这种情况,您宁愿从 python 切换到系统级语言)。 Asyncio 缺少异步编程的部分——预定义和控制上下文切换点。您实际上是在编写同步代码(即您没有考虑突然的线程切换、锁、队列等),并且当您知道调用是 IO 阻塞时使用await ...,因此您让事件循环选择其他东西,为 CPU 做好准备,稍后再选择当前状态。

这就是 asyncio 如此出色的原因——它易于维护。缺点是几乎所有“世界”也必须是异步的——数据库驱动程序、http 工具、文件处理程序。有时您会丢失库,这几乎可以保证。

【讨论】:

我不确定我是否理解这个比较。您不必了解patch_all 做了什么,就像您必须了解asyncio 是如何实现的一样;您只需编写简单、可读和可维护的同步代码(并对其进行调试),然后神奇地使其异步。我错过了什么? 你不能像同步代码一样调试补丁代码补丁代码,这就是重点。在单用户负载下运行 gevent 补丁代码,假设 100k 是不同的——就何时以及如何切换上下文而言。 asyncio 的实现(这肯定很难)与理解事件循环的概念不同。后面就很简单了。 @DanGittik “你只需编写......同步代码......,然后神奇地使其异步” - 如果您尝试这样做,您将不会从使代码异步中获得任何好处。您必须同时运行一些任务(协程)才能获得好处(example how gevent does it)。出现并发的地方迟早会出现协同程序之间的同步、死锁等问题和许多其他异步特定的事情。这些通常是不平凡的事情,asyncio 允许您比“神奇”的方法更好地阅读和调试它们。 这不是最糟糕的事情,它应该如何工作。 Golang 中的一切都是异步的,并且可以正常工作。在 Python 中,有多种选择,而且所有选择仍然以一种或另一种方式被破坏。只需比较 subprocess 和 asyncio.subprocess 即可了解标准库如何提供两种做同一事情的方法,但没有功能奇偶校验。这就是我所说的破碎。 是的,没有。所有 async 所做的只是在任何地方添加 async / await 并且您会遇到完全相同的问题,试图通过协程的执行来困惑。拥有所有额外的标记绒毛并不会使其更具可读性或可理解性。最好是简单地识别和记录异步入口点。

以上是关于Asyncio 与 Gevent [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

输出 asyncio 子进程调用的命令行?

Python基于gevent 轻松实现百万级并发

gevent常用用法

ROS ImportError:没有名为 asyncio 的模块

异步等待子处理?

优雅关闭 asyncio 协程