TL;DR
- 并发和并行解决不同的问题。 并发是关于结构化程序以处理在时间上重叠的多个任务;而并行是指在同一时刻在不同硬件上实际执行多个计算。
- 在Python中,线程和asyncio提供了没有额外CPU核心的并发。 两者都依赖于在I/O等待期间让任务让出控制,而不是与另一个线程真正同时运行Python字节码。
- 全局解释器锁(GIL)阻止Python线程将CPU密集型代码并行化。 本指南中的一个实时基准显示,四个CPU密集型任务以线程方式运行所需的时间与顺序执行大致相同(5.45秒对5.22秒),而多进程将时间缩短至在两个核心上的2.60秒。
- 对于I/O密集型工作,线程和asyncio提供了相同的收益。 相同的基准显示,八个0.5秒的等待从4.00秒顺序执行下降到使用
ThreadPoolExecutor或asyncio.gather的0.50秒。 - Python 3.13+发布了一个官方支持的、非默认的无GIL自由线程构建——这是CPython线程第一次真正能够并行化CPU密集型代码,但有一个警告是,某些C扩展包仍然强制启用GIL。
- 正确的选择由任务决定,而不是偏好。 CPU密集型工作需要多进程(或自由线程构建)以获得真正的并行性;I/O密集型工作通过线程或asyncio获得完全的并发,而根本不需要更多核心。
并发与并行:实际定义
并发是一种组织程序的方式,使多个任务可以在重叠时间内进行,即使在任何给定时刻只有一个任务实际正在执行;而并行是多个计算在不同处理单元上同时实际运行。Rob Pike的表述,在Go自己的工程博客上发表的演讲中,明确划定了界限:并发是独立执行进程的组合,而并行是计算的同时执行——并发是关于同时处理许多事情,而并行是关于一次完成许多事情。
经典的类比依然适用:一个收银员在三个顾客之间切换——为顾客A扫描物品,回答顾客B的问题,为顾客C装袋——就是并发。而三个收银员,各自完全专注于同一时刻的一个顾客,就是并行。单核机器可以运行高并发的程序(操作系统交替多个线程),而根本不会实现并行,而多核机器可以运行完全没有并发的并行代码(四个独立的、互不重叠的计算恰好在四个核心上顺序运行)。这两种属性是相互独立的,不是同一尺度上的两个点。
Python如何划定同样的界限:线程、多个进程和asyncio
Python自己的标准库文档正是围绕这个划分组织了其并发工具:“适当工具的选择将取决于要执行的任务(CPU密集型与I/O密集型)。” threading和asyncio都为Python程序提供并发——许多任务似乎同时在进展——而不需要超过一个CPU核心。multiprocessing才是真正为Python程序提供实际并行性,因为它在不同核心上运行独立的操作系统进程,每个进程都有自己的Python解释器和内存空间。
仅靠线程无法将CPU密集型工作转变为并行的原因是全局解释器锁:CPython自己的文档明确指出:“一次只能有一个线程执行Python代码”,并建议在多核机器上对CPU密集型工作使用multiprocessing或concurrent.futures.ProcessPoolExecutor,同时指出“如果您想同时运行多个I/O密集型任务,线程仍然是合适的模型。”那句话就是经典(启用GIL的)CPython的整个决策框架,下面的基准正好展示了原因。



