本章介绍了在 Python 中做并发编程的一种全新方式,这种方式使用 yield from、协程、期物和 asyncio 事件循环。首先,我们分析了两个简单的示例——两个旋转指针脚本,仔细对比了使用 threading 模块和 asyncio 包处理并发的异同。

然后,本章讨论了 asyncio.Future 类的细节,重点讲述它对 yield from 的支持,以及与协程和 asyncio.Task 类的关系。接下来分析了 asyncio 版国旗下载脚本。

然后,本章分析了 Ryan Dahl 对 I/O 延迟所做的统计数据,还说明了阻塞调用的影响。尽管有些函数必然会阻塞,但是为了让程序持续运行,有两种解决方案可用:使用多个线程,或者异步调用——后者以回调或协程的形式实现。

其实,异步库依赖于低层线程(直至内核级线程),但是这些库的用户无需创建线程,也无需知道用到了基础设施中的低层线程。在应用中,我们只需确保没有阻塞的代码,事件循环会在背后处理并发。异步系统能避免用户级线程的开销,这是它能比多线程系统管理更多并发连接的主要原因。

之后,我们又回到下载国旗的脚本,添加进度条并处理错误。这需要大幅度重构,特别是要把 asyncio.wait 函数换成 asyncio.as_completed 函数,因此不得不把 download_many 函数的大多数功能移到新添的 downloader_coro 协程中,这样我们才能使用 yield fromasyncio.as_completed 函数生成的多个期物中逐个获得结果。

然后,本章说明了如何使用 loop.run_in_executor 方法把阻塞的作业(例如保存文件)委托给线程池做。

接着,本章讨论了如何使用协程解决回调的主要问题:执行分成多步的异步任务时丢失上下文,以及缺少处理错误所需的上下文。

然后又举了一个例子,在下载国旗图像的同时获取国家名称,以此说明如何结合协程和 yield from 避免所谓的回调地狱。如果忽略 yield from 关键字,使用 yield from 结构实现异步调用的多步过程看起来类似于顺序执行的代码。

本章最后两个示例是使用 asyncio 包实现的 TCP 和 HTTP 服务器,用于按名称搜索 Unicode 字符。在分析 HTTP 服务器的最后,我们讨论了客户端 JavaScript 对服务器端提供高并发支持的重要性。使用 JavaScript,客户端可以按需发起小型请求,而不用下载较大的 HTML 页面。