Python核心编程(第3版)
第1 部分 通用应用主题

第4章 多线程编程

章节主张:多线程章节要教你的不是“多开几条线程”,而是如何管理等待、共享状态、同步原语和响应速度之间的取舍。

中文释读:这是一本应用编程书里非常关键的一章,因为很多系统一慢、一卡、一锁死,本质都和并发模型选择有关。理解 GIL、线程退出、锁和替代方案,比会写 Thread 子类更重要。

章节知识路线

  1. 4.1 简介/动机
  2. 4.2 线程和进程
  3. 4.3 线程和Python
  4. 4.4 thread模块
  5. 4.5 threading模块

关键论点

  • 线程主要解决等待和响应性,而不总是提高 CPU 性能。
  • 同步原语是共享状态治理工具。
  • 线程之外还要懂 subprocess、multiprocessing、futures 的替代路线。

行动清单

  • 先判定任务是 CPU 还是 I/O 密集。
  • 为线程设计退出条件。
  • 锁的范围要做最小化。

源章节深读

4.1 简介/动机

解决的问题:这一节解决的是“4.1 简介/动机在真实应用里到底扮演什么角色”。

第 4 章 多线程编程 123 本章将研究几种使代码更具并行性的方法。

怎么用:把这节内容转成自己的一个最小脚本、接口实验或系统边界测试,验证它在真实任务中的输入输出。

提醒:最常见的问题是只记住 4.1 简介/动机 的 API 或步骤,却没搞清它的上下游依赖与边界条件。

原文线索

第 4 章 多线程编程 123 本章将研究几种使代码更具并行性的方法。 开始的几节会讨论进程和线程的区别。 然后 介绍多线程编程的概念,并给出一些 Python 多线程编程的功能(已经熟悉多线程编程的读者 可以直接跳到 4.3.5 节)。

4.2 线程和进程

解决的问题:这一节解决的是“4.2 线程和进程在真实应用里到底扮演什么角色”。

124 第 1 部分 通用应用主题 使用多线程来规划这种编程任务可以降低程序的复杂性,使其实现更加清晰、高效、 简 洁 。

怎么用:把这节内容转成自己的一个最小脚本、接口实验或系统边界测试,验证它在真实任务中的输入输出。

提醒:最常见的问题是只记住 4.2 线程和进程 的 API 或步骤,却没搞清它的上下游依赖与边界条件。

原文线索

124 第 1 部分 通用应用主题 使用多线程来规划这种编程任务可以降低程序的复杂性,使其实现更加清晰、高效、 简 洁 。 每 个 线 程 中 的 逻 辑 都 不 复 杂 , 因 为 它 只 有 一 个 要 完 成 的 特 定 作 业 。 比 如 , UserRequestThread 的功能仅仅是读取用户输入,然后把输入数据放到队列里,以供其他线 程后续处理。

4.3 线程和Python

解决的问题:这一节解决的是“4.3 线程和Python在真实应用里到底扮演什么角色”。

第 4 章 多线程编程 125 另一个需要注意的问题是,线程无法给予公平的执行时间。

怎么用:把这节内容转成自己的一个最小脚本、接口实验或系统边界测试,验证它在真实任务中的输入输出。

提醒:最常见的问题是只记住 4.3 线程和Python 的 API 或步骤,却没搞清它的上下游依赖与边界条件。

原文线索

第 4 章 多线程编程 125 另一个需要注意的问题是,线程无法给予公平的执行时间。 这是因为一些函数会在完成 前保持阻塞状态,如果没有专门为多线程情况进行修改,会导致 CPU 的时间分配向这些贪婪 的函数倾斜。 4.3 线程和 Python 本节将讨论在如何在 Python 中使用线程,其中包括全局解释器锁对线程的限制和一个快 速的演示脚本。

4.4 thread模块

解决的问题:这一节解决的是“4.4 thread模块在真实应用里到底扮演什么角色”。

128 第 1 部分 通用应用主题 4.3.5 Python 的 threading 模块 Python 提供了多个模块来支持多线程编程,包括 thread、threading 和 Queue 模块等。

怎么用:把这节内容转成自己的一个最小脚本、接口实验或系统边界测试,验证它在真实任务中的输入输出。

提醒:最常见的问题是只记住 4.4 thread模块 的 API 或步骤,却没搞清它的上下游依赖与边界条件。

原文线索

128 第 1 部分 通用应用主题 4.3.5 Python 的 threading 模块 Python 提供了多个模块来支持多线程编程,包括 thread、threading 和 Queue 模块等。 程 序是可以使用 thread 和 threading 模块来创建与管理线程。 thread 模块提供了基本的线程和锁 定支持;而 threading 模块提供了更高级别、功能更全面的线程管理。

4.5 threading模块

解决的问题:这一节解决的是“4.5 threading模块在真实应用里到底扮演什么角色”。

132 第 1 部分 通用应用主题 个循环用于派生线程,每个线程会调用 loop()函数,并传递循环号、睡眠时间以及用于该线 程的锁这几个参数。

怎么用:把这节内容转成自己的一个最小脚本、接口实验或系统边界测试,验证它在真实任务中的输入输出。

提醒:最常见的问题是只记住 4.5 threading模块 的 API 或步骤,却没搞清它的上下游依赖与边界条件。

原文线索

132 第 1 部分 通用应用主题 个循环用于派生线程,每个线程会调用 loop()函数,并传递循环号、睡眠时间以及用于该线 程的锁这几个参数。 那么为什么我们不在上锁的循环中启动线程呢? 这有两个原因:其一, 我们想要同步线程,以便“所有的马同时冲出围栏”;其二,获取锁需要花费一点时间。

4.6 单线程和多线程执行对比

解决的问题:这一节解决的是“4.6 单线程和多线程执行对比在真实应用里到底扮演什么角色”。

140 第 1 部分 通用应用主题 4.6 单线程和多线程执行对比 示例 4-8 的 mtfacfib.py 脚本比较了递归求斐波那契、阶乘与累加函数的执行。

怎么用:把这节内容转成自己的一个最小脚本、接口实验或系统边界测试,验证它在真实任务中的输入输出。

提醒:最常见的问题是只记住 4.6 单线程和多线程执行对比 的 API 或步骤,却没搞清它的上下游依赖与边界条件。

原文线索

140 第 1 部分 通用应用主题 4.6 单线程和多线程执行对比 示例 4-8 的 mtfacfib.py 脚本比较了递归求斐波那契、阶乘与累加函数的执行。 该脚本按 照单线程的方式运行这三个函数。 之后使用多线程的方式执行同样的任务,用来说明多线程 环境的优点。

辨别题

为什么“加线程变慢了”并不罕见?

本章图版学习路径

4.1 简介/动机

线程同步与示例