用「闪电侠」的例子解释一下进程和线程


1.

艾伦在一次粒子加速器爆炸大事故中获得了极速移动的超能力,因此开始化身为超级英雄“闪电侠”。类比之下,CPU是计算机最核心的部件,它负责指令的读取和执行,每秒可以执行几十亿条指令!其实比闪电侠还要快得多。

I Am the Flash

小闪这种能力很快就被FBI发现了,为了好好利用小闪,FBI雇佣了小闪为其特别行动小组A执行任务。

说是特别行动小组,其实除了小闪之外只有一个A博士,小闪日常的工作就是取得A博士的指令并且执行。这就是计算机早期的单进程模型。

2.

特别行动小组,禁止入内

但是A博士从搜集情报到得出正确的指令毕竟需要时间,而小闪执行指令的速度又太快,所以在FBI高层眼里,A博士总是忙忙碌碌,而小闪成天优哉游哉。

为了提高小闪的利用率,FBI在特别行动小组办公室的楼下,着手成立另一个特别行动小组B。

这个着手成立的过程就是编码,而编码的结果就是得到一个能够完成某个特定功能的程序。

很快,特别行动小组B在B博士的独自掌管下开始暗中运行。这就是进程的诞生,进程其实就是运行的程序。现在FBI特别行动小组进入了多进程时代。

3.

虽然都是FBI的特别行动小组,若无特殊情况,A和B通常井水不犯河水,他们都认为自己垄断了FBI的所有资源,但这其实只是FBI的小把戏而已。

背后的含义就是每个进程采用了完全相同的虚拟地址空间,然而经由操作系统和硬件MMU协作,映射到不同的物理地址空间。

不同的进程,都有各自独立的物理内存空间,特别行动小组A和B之间的通信就是进程间通信(IPC)。

4.

小闪虽然速度快,但是依然没办法在同一时刻同时执行A博士和B博士两个人的指令,就好像人不能在向右看的同时向左看。

A博士和B博士常常为此大打出手,谁都想占用小闪更多的时间,好完成自己的KPI。

FBI领导层想了一个办法,新成立了一个调度小组,用来给各个小组分配小闪的使用时间。一开始,调度小组会给每个进程分配相等的一小段时间,然后每个小组轮番地占用小闪执行相应时间的任务。这就是CPU的时间片分配。

如果小闪在这一小段时间内还没执行完,那也必须得停,但是得保存一下执行进度,下次继续从结束的地方开始做。这就是CPU的上下文切换。

这样一来,A博士和B博士的KPI完成了,小闪也忙起来了。说是忙起来,但是花在指令执行上的时间其实也没多多少,基本就是在两个小组之间反复横跳了,小闪的工作依然惬意。

5.

两个博士眼红于小闪依然有大把的空闲时间,居然颇有默契地发起了招聘公告,广揽天下英才,势必要多找点事情给小闪做。

FBI的英雄帖

很快,行动小组内的成员越来越多,并且每个成员的任务都不一样,比如有些人负责查阅资料,有些人负责收取讯息......小组内各个成员分工协作,完成特别小组的共同目标。自此进入多线程时代。

线程就好比是小组内的成员,一个进程可以包含很多个线程。

进程是资源分配的基本单位,比如FBI给特别小组分配办公场所。

线程是CPU调度的基本单位,比如小闪需要执行每个小组成员的指令。

6.

成员多了,管理就成了一个新的问题。如果每个成员只是自说自话,完全不讲究团队协作,极容易造成团队内部冲突。

为此,FBI制定了几个策略。对应的是线程的同步。

7.

给厕所锁死

行动小组内的资源不同,共享的程度也不一样。比如厕所,当有人正在使用的时候,其他人只能等待,如果贸然闯进去必然出现冲突。这表示进程中的某些共享内存同一时间只能由一个线程使用,其他线程必须等待该线程结束使用之后才能继续使用。

一个防止其他人进入的简单方法就是给厕所添加一把锁,首先占用厕所的人上锁,其他人看到有锁之后就在门口排队,直到占用的线程释放锁才能进入。这个策略叫做「互斥锁」,英文叫做Mutex

8.

进入会议室之前先领个小可爱

不同于厕所,会议室就能同时允许10个人进入,如果人数超过10个,多出来的人只能排队等着,除非有人空出位置,其他人才能进入会议室。

为了解决这个问题,FBI在会议室的门口挂了10把钥匙,每个人进入会议室前都要取一把钥匙,出来时把钥匙放回原位。如果后来者发现没有钥匙了,就在会议室门口等待。这种策略叫做「信号量」,互斥锁只允许一个线程进入临界区,信号量允许多个线程同时进入临界区。

9.

兔子不是在睡觉,只是在等待被唤醒

有些时候,行动小组的某些任务比较复杂,需要流水线式作业。上游的人员做完之后把结果交付给下游人员处理,这就是典型的生产者消费者模式。

如果生产者生产得太快,我们可以适当让上游的人员停止作业,等待某个时机唤醒生产者;反之,如果消费者消费得太快,我们可以适当让下游的人员停止作业,等到某个时机唤醒消费者。

这种策略叫做「条件变量」,背后的原理是当线程在等待某些条件时使线程进入睡眠状态,一旦条件满足,就唤醒。

10.

频繁升级的放映机

最后拿行动小组的放映机举个例子。很多成员喜欢在休息时间坐在一起使用放映机看个电影,消遣一下时间,类似于多线程对同一资源进行读操作,这种情况下不管多少人在看电影都不会出现问题。

但是偏偏有人在其他人看电影的时候要升级一下放映机的操作系统,这肯定会影响其他人的观影体验;反之,在升级操作系统的时候,有人要看电影,这同样会对升级人员造成困扰。

这种时候我们可以定一个策略,当观影时,随时欢迎其他观影人员使用观看;当升级时,禁止任何观影人员和任何其他升级人员使用。

这种方式称为「读写锁」,也叫做「共享-独占锁」,“观影”对应的就是线程的读操作,“升级”对应的就是线程的写操作。具体来说一般有两种情况:

  1. 读写锁处于写锁定的状态,则在解锁之前,所有试图加锁的线程都会阻塞;
  2. 读写锁处于读锁定的状态,则所有试图以读模式加锁的线程都可得到访问权,但是以写模式加锁的线程则会阻塞;

公众号「蝉沐风」,欢迎关注,邂逅更多精彩文章

完!