Jakob Jenkov 2024-11-24
在多线程程序的上下文中,“并发”(Concurrency)和“并行”(Parallelism)这两个术语经常被使用。表面上看,它们似乎指的是相同的概念。但实际上,并发与并行具有不同的含义。在本教程中,我将解释这两个概念的真正含义。
需要明确的是:本文讨论的是单个应用程序(单个进程)内部的并发与并行,而不是多个应用程序、多个进程或多个计算机之间的并发与并行。
并发(Concurrency)
并发是指一个应用程序同时(或看似同时)在多个任务上取得进展。
如果计算机只有一个 CPU,那么应用程序实际上不能在同一时刻真正地在多个任务上同时取得进展。但应用程序可以通过 CPU 在不同任务之间快速切换的方式,使得多个任务看起来是“同时”进行的。这被称为并发执行。
下图说明了并发执行的情形:

并行执行(Parallel Execution)
并行执行是指当计算机拥有多个 CPU 或 CPU 核心时,可以真正地在同一时刻在多个任务上取得进展。
需要注意的是:“并行执行”并不等同于“并行性(Parallelism)”,我们稍后会详细解释“并行性”的含义。
下图展示了并行执行的情形:

并发 + 并行执行(Parallel Concurrent Execution)
也可以同时存在并发与并行执行:线程被分配到多个 CPU 上运行。
- 在同一个 CPU 上运行的线程是并发执行的;
- 在不同 CPU 上运行的线程是并行执行的。
下图说明了这种混合模式:

并行性(Parallelism)
并行性是指应用程序将一个大任务拆分成若干子任务,这些子任务可以在多个 CPU 上真正同时执行。
因此,并行性关注的是如何将单个任务分解以便并行处理,而不仅仅是多个线程在多个 CPU 上运行。
要实现真正的并行性,你的应用程序必须:
- 拥有多个线程;
- 每个线程运行在不同的 CPU / CPU 核心 / GPU 核心等上。
下图展示了一个大任务被拆分为 4 个子任务,并由 4 个线程在 2 个 CPU 上执行:
- 同一 CPU 上的子任务是并发执行的;
- 不同 CPU 上的子任务是并行执行的。

如果这 4 个子任务分别运行在 4 个独立的 CPU 上,那就是完全并行。但在实际中,任务通常被自然地划分为适合逻辑结构的子任务数量,然后由线程调度器负责将线程分配到可用的 CPU 上。
并发与并行的组合方式
总结一下:
- 并发:单个 CPU 通过任务切换,使多个任务看似同时进行;
- 并行性:将一个任务拆分为多个子任务,真正同时在多个 CPU 上执行。
这两种执行模型可以在同一个应用程序中组合使用。以下是几种典型组合:
1. 并发,但不并行(Concurrent, Not Parallel)
应用程序在多个任务之间切换执行(并发),但没有将任何任务拆分以并行执行。所有任务都在单个 CPU 上交替运行。
2. 并行,但不并发(Parallel, Not Concurrent)
应用程序一次只处理一个任务,但会将该任务拆分为多个子任务并行执行。当前任务完全完成后,才开始处理下一个任务。
3. 既不并发,也不并行(Neither Concurrent Nor Parallel)
应用程序一次只处理一个任务,且不拆分任务,也不与其他任务交替执行。例如一些简单的命令行工具,任务太小,无需并发或并行。
4. 既并发,又并行(Concurrent and Parallel)
有两种情况:
- 简单情况:启动多个线程,并分配到多个 CPU 上运行(即“并发+并行执行”);
- 复杂情况:应用程序同时处理多个任务(并发),并且每个任务又被拆分为子任务并行执行。
⚠️ 注意:虽然这种组合听起来很强大,但不一定带来显著性能提升。因为仅靠并发或仅靠并行通常已能充分利用 CPU 资源。盲目结合两者可能导致性能增益微弱,甚至因调度开销而降低性能。务必在采用前进行分析和测量。