背景
我曾经在一个使用Python2编写的系统上工作过,其中有很多自定义的同步I/O代码,并且使用线程进行扩展。某个时刻,我们无法进一步扩展它,意识到我们必须转向异步编程。
- Twisted 是一个受欢迎的选择,但我们想避免其回调地狱。
- 它确实有
@inlineCallbacks
修饰符,有效地使用生成器魔法实现协程,一些其他库也是如此。那更容易被容忍,但感觉有点不太稳定。 - 然后我们发现了gevent。你所要做的就是:
from gevent import monkey
monkey.patch_all()
就像这样,你所有的标准I/O、套接字、数据库事务以及所有纯Python编写的内容都是异步的,使用greenlets在幕后进行yield和切换。
它并不完美:
- 那时,在Windows上它工作得不好(今天它仍然有一些限制)。幸运的是,我们正在运行Linux系统。
- 它无法monkey-patch C扩展库,因此我们不能使用MySQLdb。幸运的是,有许多纯Python的替代品,比如PyMySQL。
问题
现在,Python 3更受欢迎,而伴随着它的是asyncio。个人认为它很棒,但最近有人问我它与我们用gevent实现的方式有哪些不同,并且我没有想到足够好的答案。
这听起来可能很主观,但实际上我正在寻找一个真实的应用场景,在这个场景中,一个会明显超过另一个,或者允许其他不能做的事情。以下是我迄今为止收集到的考虑:
就像我所说的,gevent在Windows上有一些限制。不过,我知道的大多数生产代码都运行在Linux上。
如果你需要在Windows上运行,请使用asyncio。
Gevent无法monkey-patch C扩展库。但是,asyncio无法monkey-patch 任何东西。
想象有一个新的数据库技术出现了,你想使用它,但是没有用于它的纯Python库,所以你无法将其集成到Gevent中。问题是当没有一个io*库可以与asyncio集成时,你也是被卡住的!当然有工作线程和执行器,但这不是重点,而且在两种情况下效果都很好。
有些人说这是个人口味的问题,但我认为同步编程本质上比异步编程更容易(想一想:你是否曾经遇到过一个初学者程序员,他可以使用套接字,但很难正确理解如何选择/轮询,或者思考future/promise?反之亦然)。
无论如何,我们不会深入讨论这一点。我想提到这一点是因为它经常出现(这里有一个reddit上的讨论),但我真正想知道的是何时使用其中之一具有实际原因。
asyncio是标准库的一部分。这非常重要:这意味着它得到了很好的维护,很好地记录,并且每个人都知道它并默认使用它。
但是,考虑到你需要了解多少Gevent才能使用它(而且它也很好地得到了维护和记录),似乎并不是很关键。因此,虽然即使涉及到future的最复杂的情况,StackOverflow上也有多个答案,但完全不使用future的可能性似乎同样可行。