You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

66 lines
6.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 并发和并行
> 并发是同一时间应对多件事情的能力 - [Rob Pike](https://baike.baidu.com/item/罗布·派克/10983505)
并行和并发其实并不难,但是也给一些用户造成了困扰,因此我们专门开辟一个章节,用于讲清楚这两者的区别。
`Erlang`之父[`Joe Armstrong`](https://en.wikipedia.org/wiki/Joe_Armstrong_(programmer))(伟大的异步编程先驱开创一个时代的殿堂级计算机科学家我还犹记得当年刚学到erlang时的震撼respect!)用一张5岁小孩都能看到的图片解释了并发与并行的区别:
<img alt="" src="/img/threads-01.png" class="center" />
上图很直观的体现了:
- 并发是多个队列使用同一个咖啡机,然后两个队列轮换着使用(未必是1:1轮换也可能是其它轮换规则),最终每个人都能接到咖啡
- 并行是每个队列都拥有一个咖啡机,最终也是每个人都能接到咖啡,但是效率更高,因为同时可以有两个人在接咖啡
当然,我们还可以对比下串行:只有一个队列且仅使用一台咖啡机,哪怕前面那个人接咖啡时突然发呆了几分钟,后面的人也只能等他结束才能继续接。可能有读者有疑问了,从图片来看,并发也存在这个问题啊,前面的人抽了几分钟不接怎么办?很简单,另外一个队列的人把他推开就行了,自己队友不能在背后开枪,但是其它队的可以:)
在正式开始之前,先给出一个结论:**并发和并行都是对"多任务"处理的描述,其中并发是轮流处理,而并行是同时处理**。
## CPU多核
现在的个人计算机动辄拥有十来个核心(M1 Max/Itel 12代)如果使用串行的方式那真是太低调了因此我们把各种任务简单分成多个队列每个队列都交给一个cpu核心去执行当某个cpu核心没有任务时它还能去其它核心的队列中偷任务(真·老黄牛),这样就实现了并行化处理。
#### 单核心并发
那问题来了在早期只有一个CPU核心时我们的任务是怎么处理的呢其实聪明的读者应该已经想到是的并发解君愁。当然这里还得提到操作系统的多线程正是操作系统多线程 + CPU核心才实现了现代化的多任务操作系统。
在OS级别多线程负责管理我们的任务队列你可以简单认为一个线程管理着一个任务队列然后线程之间还能根据空闲度进行任务调度。我们的程序只会跟OS线程打交道并不关心CPU到底有多少个核心真正关心的只是OS当线程把任务交给CPU核心去执行时如果只有一个CPU核心那么它就只能同时处理一个任务。
相信大家都看出来了:**CPU核心**对应的是上图的咖啡机,而**多个线程的任务队列**就对应的多个排队的队列最终受限于CPU核心数, 每次只会有一个任务被处理。
和排队一样,假如某个任务执行时间过长,就会导致用户界面的假死(相信使用windows的同学或多或少都碰到或者假死的问题) 那么就需要CPU的任务调度了(真实CPU和调度很复杂我们这里做了简化),有一个调度器会按照某些条件从队列中选择任务进行执行,并且当一个任务执行时间过长时,会强行切换该任务到后台中(或者放入任务队列,真实情况很复杂!),去执行新的任务。
不断这样的快速任务切换对用户而言就实现了表面上的多任务同时处理但是实际上最终也只有一个CPU核心在不停的工作。
因此并发的关键在于:**快速轮换处理不同的任务**,给用户带来所有任务同时在运行的假象。
#### 多核心并行
当CPU核心增多到`N`时,那么同一时间就能有`N`个任务被处理,那么我们的并行度就是`N`,相应的处理效率也变成了单核心的`N`倍(实际情况并没有这么高)。
#### 多核心并发
当核心增多到`N`时,操作系统同时在进行的任务肯定远不止`N`个,而这些任务将被放入`M`个线程队列中,接着交给`N`个CPU核心去执行最后实现了`M:N`的处理模型,在这种情况下,**并发跟并行时同时在发生的,所有用户任务从表面来看都在并发的运行,其实实际上,同一时刻只有`N`个任务能被同时并行的处理**。
看到这里,相信大家已经明白两者的区别,那么我们下面给出一个正式的定义(该定义摘选自<<并发的艺术>>)。
## 正式的定义
如果某个系统支持两个或者多个动作的同时存在,那么这个系统就是一个并发系统。如果某个系统支持两个或者多个动作同时执行,那么这个系统就是一个并行系统。并发系统与并行系统这两个定义之间的关键差异在于**“存在”**这个词。
在并发程序中可以同时拥有两个或者多个线程。这意味着,如果程序在单核处理器上运行,那么这两个线程将交替地换入或者换出内存。这些线程是**同时“存在”**的——每个线程都处于执行过程中的某个状态。如果程序能够并行执行,那么就一定是运行在多核处理器上。此时,程序中的每个线程都将分配到一个独立的处理器核上,因此可以同时运行。
相信你已经能够得出结论——**“并行”概念是“并发”概念的一个子集**。也就是说,你可以编写一个拥有多个线程或者进程的并发程序,但如果没有多核处理器来执行这个程序,那么就不能以并行方式来运行代码。因此,凡是在求解单个问题时涉及多个执行流程的编程模式或者执行行为,都属于并发编程的范畴。
## 本书的选择
由于对于编程而言,并行度是无法控制的,我们能控制的仅仅是并发,因此出于简单性的原则,在本书中,我们将统一使用并发来描述问题,除非因为某些特定场景需要时,才进行并发和并行的区分。