术语并发和并行通常用于多线程程序。最初,并发和并行似乎是同一个概念。不过,并发和并行实际上是有不同的含义。本教程将解释这些概念的含义。

为了清晰起见,本文中我将研究在单个应用程序(一个进程)中的并发和并行,而不是在多个应用程序、进程或者计算机之间的并发和并行。

并发

并发(Concurrency)是指应用程序正在同时或者至少看起来像是在同时(并发地)进行多个任务。

如果计算机只有一个 CPU,那么应用程序可能没法真正同时进行多个任务,而是一次执行应用程序内的一个任务。为了同时运行多个任务,CPU 在执行期间在不同任务之间切换。如下图所示:

6. 并发与并行 - 图1

并行执行(Parallel Execution)

并行执行(Parallel Execution)是计算机有多个 CPU 或者 CPU 核,并且同时执行多个任务。不过,并行执行与并行(parallelism)并不是指同一个现象。我会稍后再谈并行。并行执行示意图如下:

6. 并发与并行 - 图2

并行并发执行

并行并发执行也是可能的,其中线程分布在多个 CPU之间。因此,在同一个 CPU 上执行的线程是并发执行的,而在不同 CPU 上执行的线程是并行执行的。并行并发执行示意图如下:

6. 并发与并行 - 图3

并行(Parallelism)

术语并行(Parallelism)是指一个应用程序把它的任务拆分成更小的子任务,然后这些子任务可以并行处理,比如在多个 CPU 上同时处理。因此,并行与并行并发执行不是同一个执行模型 - 即使它们表面上看起来很相似。

要实现真正的并行,应用程序必须有多个正在运行的线程,每个线程必须在单独的 CPU、CPU 核、图形卡 GPU 核或者类似的上面运行。

下图展示一个较大的任务被拆分为 4 个子任务。这四个子任务被 4 个不同的线程执行,4 个线程运行在 2 个不同的 CPU 上。这意味着,这些子任务的一部分是并发执行的(在同一个 CPU 上执行的那些子任务),一部分是并行执行的(在不同的 CPU 上执行的那些子任务)。

6. 并发与并行 - 图4

如果是 4 个子任务由 4 个运行各自的 CPU 上的线程执行(总共 4 个 CPU),那么任务执行已经完全是并行执行。不过,要把一个任务拆分成与可用 CPU 数一样多的子任务并不总是容易的。通常,把一个任务拆分成自然符合手头的任务的子任务数比较容易,然后让线程调度器负责在可用的 CPU 之间分配线程。

并发和并行组合

重复一下,并发(Concurrency)是指一个 CPU 如何看起来像在同时(又称并发地)进行多个任务。

而并行(Parallelism)与应用程序如何并行化一个任务的执行有关,通常是把任务拆分成子任务,然后子任务可以并行完成。

这两种执行风格可以在同一个应用程序中组合。我在下面小节介绍其中一些组合。

并发,不并行

一个应用程序可以是并发,但不是并行的。也就是说,它看起来像同时(并发地)进行多个任务,但是应用程序在进行每个任务之间切换 - 直到任务完成为止。并没有真正的以并行线程或者 CPU 执行的并行执行任务。

并行,不并发

应用程序也可以是并行,但不是并发的。也就是说,应用程序一次只处理一个任务,并且这个任务被拆分成可以并行处理的子任务。不过,每个任务(以及子任务)都是在下一个任务被拆分以及并行执行之前完成的。

既不并发,也不并行

此外,应用程序可以既不是并发,也不是并行。也就是说,一次处理一个任务,并且任务也不会被分拆成并行处理的子任务。小的命令行应用程序可能就是这样情况,因为它只有一个作业,太小,并行化没有意义。

并发和并行

最后,应用程序也可以同时是并发和并行的,有两种方式:

第一种方式是,简单的并行并发执行。如果应用程序启动多个线程,然后在多个 CPU 上执行这些线程,就是这种情况。

第二种方式是,应用程序既并发处理多个任务,又把每个任务拆分成并行执行的子任务。不过,在这种情况下,并发和并行的一些好处可能就丢失了,因为计算机中的 CPU 已经是相当繁忙地要么单独处理并发,要么单独处理并行。把二者组合起来可能只会带来很小的性能增益,甚至是性能损失。在盲目采用并发并行模型之前,一定要进行分析和测量。