第 18 章 使用 asyncio 包处理并发

并发是指一次处理多件事。
并行是指一次做多件事。
二者不同,但是有联系。
一个关于结构,一个关于执行。
并发用于制定方案,用来解决可能(但未必)并行的问题。1

——Rob Pike
Go 语言的创造者之一

1摘自“Concurrency Is Not Parallelism (It's Better)”(http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-5)演讲的第 5 张幻灯片。

Imre Simon 教授 2 说过,科学界有两个重要过错:使用不同的词表示相同的事物,以及使用同一个词表示不同的事物。如果你研究过并发编程或并行编程,会发现“并发”和“并行”有不同的定义。我将采用上述引文中 Rob Pike 的非正式定义。

2Imre Simon(1943—2009)是巴西的计算机科学先驱,对自动机理论(Automata Theory)有杰出的贡献,开创了热带数学(Tropical Mathematics)这一领域。他还是自由软件和自由文化的拥护者。我有幸曾与他一起学习、工作和相处。

真正的并行需要多个核心。现代的笔记本电脑有4个 CPU 核心,但是通常不经意间就有超过 100 个进程同时运行。因此,实际上大多数过程都是并发处理的,而不是并行处理。计算机始终运行着 100 多个进程,确保每个进程都有机会取得进展,不过 CPU 本身同时做的事情不能超过四件。十年前使用的设备也能并发处理 100 个进程,不过都在同一个核心里。鉴于此,Rob Pike 才把那次演讲取名为“Concurrency Is Not Parallelism (It's Better)”[“并发不是并行(并发更好)”]。

本章介绍 asyncio 包,这个包使用事件循环驱动的协程实现并发。这是 Python 中最大也是最具雄心壮志的库之一。Guido van Rossum 在 Python 仓库之外开发 asyncio 包,把这个项目的代号命名为“Tulip”(郁金香)。因此,在网上搜索这方面的资料时,会经常看到这种花的名称。例如,这个项目的主要讨论组仍叫 python-tulip(https://groups.google.com/forum/#!forum/python-tulip)。

Python 3.4 把 Tulip 添加到标准库中时,把它重命名为 asyncio。这个包也兼容 Python 3.3,在 PyPI 中可以通过新的官方名称找到(https://pypi.python.org/pypi/asyncio)。asyncio 大量使用 yield from 表达式,因此与 Python 旧版不兼容。

 Trollius 项目(也以花名命名,http://trollius.readthedocs.org/)移植了 asyncio,把 yield from 替换成 yield 和精巧的回调(FromReturn),以便支持 Python 2.6 及以上版本。yield from ... 表达式变成了 yield From(...);如果协程需要返回结果,那么要把 return result 替换成 raise Return(result)。Trollius 由 Victor Stinner 主导,他也是 asyncio 包的核心开发者。Victor 人很好,在本书付梓之前同意审核本章。

本章讨论以下话题:

首先,本章通过简单的示例来对比 threading 模块和 asyncio 包。