摘要:并发与并行是两个易混淆但本质不同的概念。并发指多个任务执行过程重叠,关注共享状态管理;并行则是将任务拆分为子任务同时执行,旨在提升硬件性能。并发系统需满足正确性、安全性和活性等特性,实现时需解决数据共享和I/O资源共享问题。并行通过任务拆分可显著提升执行效率,常见于多核处理器和显卡等场景。单核处理器通过上下文切换模拟并发,而多核处理器能真正实现并行处理。理解二者的区别有助于在软件设计中做出合理决策。
目录
并发与并行
深入理解并发
并发的层级
并发系统的特性
并发系统的实现障碍
示例:获取网页加载耗时
main.py
输出结果
什么是并行?
并行的必要性
实现并发 / 并行的处理器基础
单核处理器
多核处理器
取指 – 译码 – 执行周期
并发与并行
并发和并行均与多线程程序相关,但人们对二者的异同点一直存在诸多混淆。核心问题在于:并发是否等同于并行?尽管这两个术语看似相近,但答案是否定的,二者并非同一概念。那么,二者的根本区别究竟是什么?
简单来说,并发处理的是不同线程对共享状态的访问管理,而并行则是利用多个 CPU 或其核心来提升硬件性能。
深入理解并发
并发指两个任务的执行过程出现重叠,即一个应用程序能同时推进多个任务的执行。我们可以通过图示理解:多个任务在同一时间内都处于推进状态。

并发的层级
从编程角度来看,并发主要分为三个重要层级:
并发系统的特性
一个程序或并发系统要保证正确性,必须满足以下特性,其中与系统终止相关的特性如下:
并发系统的实现障碍
开发人员在实现并发系统时,必须考虑以下两个关键问题,它们也是并发系统的主要实现障碍:
数据共享这是实现并发系统的核心问题:多个线程或进程之间的数据共享。开发人员必须通过锁机制保护共享数据,确保所有对共享数据的访问都按顺序执行,同一时间只有一个线程或进程能访问共享数据。若多个线程或进程同时尝试访问同一共享数据,除其中一个外,其余都会被阻塞并处于空闲状态。也就是说,锁生效时,同一时间只能使用一个进程或线程。针对这一障碍,有以下几种简易解决办法:
- 限制数据共享:最直接的方案是不共享任何可变数据,此时无需使用显式锁,因数据共享产生的并发障碍也会随之解决。
- 借助数据结构:并发进程往往需要同时访问同一数据,除显式锁外,还可使用支持并发访问的数据结构。例如 Python 的queue模块提供了线程安全的队列,也可使用multiprocessing.JoinableQueue类实现基于多进程的并发。
- 传递不可变数据:若当前使用的并发队列等数据结构不合适,可直接传递不可变数据,无需为其加锁。
- 传递只读可变数据:若业务需要传递可变数据而非不可变数据,可传递只读的可变数据。
I/O 资源共享实现并发系统的另一重要问题是线程或进程对 I/O 资源的使用。当一个线程或进程长时间占用 I/O 资源,其他线程 / 进程只能处于空闲状态时,就会出现该问题,这类障碍在 I/O 密集型应用中尤为常见。以浏览器请求网页为例:网页请求属于典型的 I/O 密集型操作,若数据的请求速率低于消费速率,并发系统中就会出现 I/O 障碍。
示例:获取网页加载耗时
以下 Python 脚本用于请求一个网页,并统计网络获取该网页的耗时:
main.py
import urllib.request
import time
ts = time.time()
req = urllib.request.urlopen('https://www.tutorialspoint.com')
pageHtml = req.read()
te = time.time()
print("页面获取耗时:{} 秒".format(te – ts))
输出结果
运行上述脚本后,可得到如下页面获取耗时:
plaintext
页面获取耗时:0.15655970573425293 秒
可见,单次获取网页的耗时就超过了 0.1 秒。若需要获取数千个不同的网页,网络耗时会大幅增加,这也体现了并发 / 并行优化的必要性。
什么是并行?
并行可定义为:将一个任务拆分为多个子任务,使这些子任务能同时被处理的设计思路。这与前文所说的并发形成区别 —— 并发是多个事件在同一时间发生,而并行是对单个任务的拆分与同步处理。
我们可通过图示理解:一个任务被拆分为多个子任务,所有子任务并行执行。
为进一步区分并发与并行,可分为以下四种情况:
并行的必要性
我们可将子任务分配到单个 CPU 的多个核心,或网络中的多台计算机上,以此实现并行。理解并行的必要性,可参考以下几点:
现实中的并行案例:计算机的显卡是体现并行处理强大能力的典型例子。显卡拥有数百个独立的处理核心,这些核心可独立运行、同时执行任务,正因如此,我们才能流畅运行大型应用程序和游戏。
实现并发 / 并行的处理器基础
了解了并发、并行的概念及二者的区别后,还需掌握其运行的硬件系统知识 —— 这能帮助我们在软件设计时做出合理决策。处理器主要分为以下两类:
单核处理器
单核处理器在任意给定时间内,只能执行一个线程。这类处理器通过上下文切换机制,保存某个线程在特定时间的所有必要信息,后续再恢复这些信息继续执行。上下文切换能让处理器在一秒内推进多个线程的执行,从外部看,系统仿佛在同时处理多个任务。
单核处理器的优势:功耗更低,且多个核心间无需复杂的通信协议。单核处理器的劣势:运行速度受限,无法适配大型应用程序。
多核处理器
多核处理器拥有多个独立的处理单元,即核心。这类处理器无需上下文切换机制,因为每个核心都具备执行存储指令序列所需的全部资源。
取指 – 译码 – 执行周期
多核处理器的每个核心都遵循取指 – 译码 – 执行(Fetch-Decode-Execute) 周期执行指令,该周期分为三个步骤:
多核处理器的优势:执行速度远快于单核处理器,适合运行大型应用程序。多核处理器的劣势:多个核心间的通信协议复杂,且功耗远高于单核处理器。





