并行与并发的区别

[转载]并行与并发的区别

本文转载自https://laike9m.com/blog/huan-zai-yi-huo-bing-fa-he-bing-xing,61/,仅做个人学习使用,侵权请联系删除

“并发” 指的是程序的结构,“并行” 指的是程序运行时的状态

并行(parallelism)

这个概念很好理解。所谓并行,就是同时执行的意思,无需过度解读。判断程序是否处于并行的状态,就看同一时刻是否有超过一个 “工作单位” 在运行就好了。所以,单线程永远无法达到并行状态

要达到并行状态,最简单的就是利用多线程和多进程。但是 Python 的多线程由于存在著名的 GIL,无法让两个线程真正 “同时运行”,所以实际上是无法到达并行状态的。

并发(concurrency)

要理解 “并发” 这个概念,必须得清楚,并发指的是程序的 “结构”。当我们说这个程序是并发的,实际上,这句话应当表述成 “这个程序采用了支持并发的设计”。好,既然并发指的是人为设计的结构,那么怎样的程序结构才叫做支持并发的设计?

正确的并发设计的标准是:使多个操作可以在重叠的时间段内进行 (two tasks can start, run, and complete in overlapping time periods)

这句话的重点有两个。我们先看 “(操作)在重叠的时间段内进行” 这个概念。它是否就是我们前面说到的并行呢?是,也不是。并行,当然是在重叠的时间段内执行,但是另外一种执行模式,也属于在重叠时间段内进行。这就是协程。

使用协程时,程序的执行看起来往往是这个样子:

并行与并发的区别_第1张图片

task1, task2 是两段不同的代码,比如两个函数,其中黑色块代表某段代码正在执行。注意,这里从始至终,在任何一个时间点上都只有一段代码在执行,但是,由于 task1 和 task2 在重叠的时间段内执行,所以这是一个支持并发的设计。与并行不同,单核单线程能支持并发。

经常看到这样一个说法,叫做并发执行。现在我们可以正确理解它。有两种可能:

  1. 原本想说的是 “并行执行”,但是用错了词
  2. 指多个操作可以在重叠的时间段内进行,即,真的并行,或是类似上图那样的执行模式。

我的建议是尽可能不使用这个词,容易造成误会,尤其是对那些并发并行不分的人。但是读到这里的各位显然能正确区分,所以下面为了简便,将使用并发执行这个词。

第二个重点是 “可以在重叠的时间段内进行”中的 “可以” 两个字。“可以”的意思是,正确的并发设计使并发执行成为可能,但是程序在实际运行时却不一定会出现多个任务执行时间段 overlap 的情形。比如:我们的程序会为每个任务开一个线程或者协程,只有一个任务时,显然不会出现多个任务执行时间段重叠的情况,有多个任务时,就会出现了。这里我们看到,并发并不描述程序执行的状态,它描述的是一种设计,是程序的结构,比如上面例子里 “为每个任务开一个线程” 的设计。并发设计和程序实际执行情况没有直接关联,但是正确的并发设计让并发执行成为可能.反之,如果程序被设计为执行完一个任务再接着执行下一个,那就不是并发设计了,因为做不到并发执行。

那么,如何实现支持并发的设计?两个字:拆分

之所以并发设计往往需要把流程拆开,是因为如果不拆分也就不可能在同一时间段进行多个任务了。这种拆分可以是平行的拆分,比如抽象成同类的任务,也可以是不平行的,比如分为多个步骤。

并发和并行的关系

Different concurrent designs enable different ways to parallelize.

这句话来自著名的 talk: Concurrency is not parallelism。它足够 concise,以至于不需要过多解释。但是仅仅引用别人的话总是不太好,所以我再用之前文字的总结来说明:并发设计让并发执行成为可能,而并行是并发执行的一种模式

最关键的一点是,计算机在不同层次上都使用了并行技术。之前我讨论的实际上仅限于 Task-Level 这一层,在这一层上,并行无疑是并发的一个子集。但是并行并非并发的子集,因为在 Bit-Level 和 Instruction-Level 上的并行不属于并发——比如引文中举的 32 位计算机执行 32 位数加法的例子,同时处理 4 个字节显然是一种并行,但是它们都属于 32 位加法这一个任务,并不存在多个任务,也就根本没有并发。

所以,正确的说法是这样:
并行指物理上同时执行,并发指能够让多个任务在逻辑上交织执行的程序设计

按照我现在的理解,并发针对的是 Task-Level 及更高层,并行则不限。这也是它们的区别。

你可能感兴趣的:(并行与并发的区别)