1. 生产者消费者问题
系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。
生产者、消费者共享一个初始为空、大小为 n \displaystyle{ n } n 的缓冲区。
只有缓冲区没满 时,生产者才能将产品放入缓冲区,否则等待。
只有缓冲区不空 时,消费者才能取出产品,否则等待。
缓冲区是临界资源,各进程必须互斥地访问 。
信号量机制可以实现互斥、同步、对一类系统资源的申请和释放
graph LR
subgraph buffer["缓冲区"]
direction TB
end
A(["生产者进程"])-->buffer
buffer-->B(["消费者进程"])
%%{init: {'theme':'dark'}}%%
graph LR
subgraph buffer["缓冲区"]
direction TB
end
A(["生产者进程"])-->buffer
buffer-->B(["消费者进程"])
semaphore mutex = 1 ; // 互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n; // 同步信号量,表示空闲缓冲区的数量
semaphore full = 0 ; // 同步信号量,表示产品的数量,即非空缓冲区的数量
实现互斥的 P 操作一定要在实现同步的 P 操作之后。
2. 多生产者和多消费者问题
m \displaystyle{ m } m 个生产者和 n \displaystyle{ n } n 个消费者共享 k \displaystyle{ k } k 件产品缓冲区
semaphore empty = k, full = 0 ; // 缓冲区空闲数和填充数
semaphore mutex = 1 ; // 互斥信号
int in = 0 , out = 0 ; // 放入和取出缓冲区指针
in = (in + 1 ) % k; // 移动到下一个空间
out = (out + 1 ) % k; // 移动到下一个空间
3. 吸烟者问题(单生产者多消费者)
假设一个系统有三个吸烟者和一个供应者。第一个拥有烟草,第二个拥有纸,第三个拥有胶水。供应者进程无限的提供三种材料,每次将两种材料放到桌上。拥有剩下那种材料的吸烟者会拿材料并卷烟抽掉。完成后会给供应者发出完成信号,供应者会准备另外两种材料。
同步关系中前 V 后 P
4. 读者写者问题
4.1. 问题描述
在存储空间或最经典的数据库 的读写中,当有读者对共享资源进行访问时,其他读者也可进行访问 ,但写者无法对其进行修改。另一方面,当有写者正在进行写操作时,其他读者写者均不能访问该资源 。
4.2. 问题解决
4.2.1. 方案 1
该方案能够达到在有读者在访问时,阻塞写者;当有写者在写入时,阻塞其他进程 。但是如果不断地有读者进入进程,写者就会饥饿。该方案被称为 “读者优先”。
semaphore writerblock = 1 , mutex = 1 ;
if (readcount == 1 ) // 如果当前进入的是第一个读者,那么就阻塞后面的写者
if (readcount == 0 ) // 如果是最后一个退出的读者,则唤醒被阻塞的写者
4.2.2. 方案 2
针对上述写者饥饿的问题,在此处给出一个解决方案。即:当有一个写者到来时,会将晚于这个写者的读者挡住。
semaphore writerblock = 1 , readerblock = 1 , mutex = 1 ;
P(readerblock) ; // 带有被阻塞限制的访问共享资源
if (readcount == 1 ) // 如果当前进入的是第一个读者,那么就阻塞后面的写者
if (readcount == 0 ) // 如果是最后一个退出的读者,则唤醒被阻塞的写者
P(readerblock) ; // 阻塞后来的读者
V(readerblock) ; // 唤醒后来的读者
5. 睡眠理发师问题
5.1. 问题描述
理发店有一名理发师,一把理发椅和 n 把供等候的椅子。当空闲时,理发师会躺在椅子上睡觉。有顾客来时,会将理发师唤醒。当有顾客正在理发时,新的顾客来了,会在坐在等候椅上等待。如果没有等候椅,则刚刚新来的顾客离开。
5.2. 问题解决
对于顾客来说,他们需要抢占的资源为“等候椅”。当有空闲的椅子时,就能在此等待,并唤醒理发师。而对于理发师,首先判断是否有顾客,若没有则睡眠;若有,则会在处理完当前顾客后继续处理下一个顾客。
int waiting = 0 , chairs = N; // 等待者人数,椅子数
semaphore customers = 0 , barbers = 0 , mutex = 1 ;
P(customers) ; // 判断是否有顾客,没有则理发师睡眠
V(barbers) ; // 理发师准备为顾客理发
if (waiting < chairs) { // 判断是否有空椅子
6. 五位哲学家就餐问题
6.1. 问题描述
五名哲学家坐在圆桌上就餐,食物供应充足,但每个人左右各只有一支筷子,即总共只有五支筷子。
6.2. 问题分析
每个人可以先拿起一侧的筷子,再拿起另一侧的筷子,然后再就餐。但与此同时也出现了问题,即当所有人同时拿起左手的筷子,如果不退避,则会造成死锁。因此,可以让一部分人先拿起右边的筷子,这样就打破了环路。
semaphore stick [ 5 ] = { 1 , 1 , 1 , 1 , 1 };
process philosopher ( int i ) {
7. 独木桥问题
独木桥问题 1
东西向汽车驶过独木桥,为保证交通安全,只要桥上无车,则允许一方的汽车过桥。等待全部过完后才允许另一方的汽车过桥。请用信号量和 PV 操作写出汽车过独木桥问题的同步算法。
semaphore a_mutex = 1 , b_mutex = 1 ;
semaphore a_count_mutex = 1 , b_count_mutex = 1 ;
int a_count = 0 , b_count = 0 ;
P(a_count_mutex) ; // a_count 互斥上锁
if (a_count_mutex == 1 ) // A 侧有车来
V(a_count_mutex) ; // a_count 解锁
P(a_count_mutex) ; // a_count 互斥上锁
if (a_count_mutex == 0 ) // A 侧没有车了
P(b_count_mutex) ; // b_count 互斥上锁
if (b_count_mutex == 1 ) // B 侧有车来
V(b_count_mutex) ; // b_count 解锁
P(b_count_mutex) ; // b_count 互斥上锁
if (b_count_mutex == 0 ) // B 侧没有车了
独木桥问题 2
在独木桥问题 1 中,限制桥面上最多可以有 k 辆汽车通过 ,使用信号量和 PV 操作写出汽车过独木桥问题的同步算法。
semaphore a_mutex = 1 , b_mutex = 1 ;
semaphore a_count_mutex = 1 , b_count_mutex = 1 ;
int a_count = 0 , b_count = 0 ;
P(a_count_mutex) ; // a_count 互斥上锁
if (a_count_mutex == 1 ) // A 侧有车来
if (a_count_mutex == k) // 达到 k 辆车
V(a_count_mutex) ; // a_count 解锁
P(a_count_mutex) ; // a_count 互斥上锁
if (a_count_mutex < k) // 少于 k 辆车
V(a_mutex) ; // A 侧车恢复上桥 ? 会不会造成 a_mutex 大于 1 ?
if (a_count_mutex == 0 ) // A 侧没有车
P(b_count_mutex) ; // b_count 互斥上锁
if (b_count_mutex == 1 ) // B 侧有车来
if (b_count_mutex == k) // 达到 k 辆车
V(b_count_mutex) ; // b_count 解锁
P(b_count_mutex) ; // b_count 互斥上锁
if (b_count_mutex < k) // 少于 k 辆车
if (b_count_mutex == 0 ) // B 侧没有车
独木桥问题 3
在独木桥问题 1 中,要求保证东西方向交替通过一辆汽车。使用信号量和 PV 操作写出汽车过独木桥问题的同步算法。
独木桥问题 4
在独木桥问题 1 中,要求各方向的汽车串行过桥。但当另一方提出过桥请求时,应能阻止对方尚未上桥的后继车辆,待桥面上的车过完桥后,另一方的汽车开始过桥。使用信号量和 PV 操作写出汽车过独木桥问题的同步算法。
semaphore a_mutex = 1 , b_mutex = 1 ;
semaphore a_count_mutex = 1 , b_count_mutex = 1 ;
int a_count = 0 , b_count = 0 ;
P(b_mutex) ; // 禁止 B 上桥 ??
P(a_count_mutex) ; // a_count 互斥上锁
if (a_count_mutex == 1 ) // A 侧有车来
V(a_count_mutex) ; // a_count 解锁
V(b_mutex) ; // 恢复 B 上桥 ??
P(a_count_mutex) ; // a_count 互斥上锁
if (a_count_mutex == 0 ) // A 侧没有车了
P(a_mutex) ; // 禁止 A 上桥 ??
P(b_count_mutex) ; // b_count 互斥上锁
if (b_count_mutex == 1 ) // B 侧有车来
V(b_count_mutex) ; // b_count 解锁
V(a_mutex) ; // 恢复 A 上桥 ??
P(b_count_mutex) ; // b_count 互斥上锁
if (b_count_mutex == 0 ) // B 侧没有车了
安全岛
现有一个如下图所示的小巷,除安全岛可容 2 人暂时停身外,仅能容 1 人通过。若A、B 两头都允许行人进和出,使用信号量与 PV 操作设计一个算法让两头的行人顺利通过小巷。
A============S============B
semaphore mutex_a_s = 1 , mutex_b_s = 1 ;
semaphore mutex_a = 1 , mutex_b = 1 ;
8. 苹果橘子问题
桌子上有一只盘子,每次只能放一个水果。有人想放苹果,有人想放橘子,有人想拿苹果,拿橘子等。
graph LR
subgraph producer
direction TB
A([生产者1])
C([生产者2])
end
subgraph consumer
direction TB
D([消费者1])
E([消费者1])
end
B[缓冲区]
A-->B
C-->B
B-->D
B-->E
%%{init: {'theme':'dark'}}%%
graph LR
subgraph producer
direction TB
A([生产者1])
C([生产者2])
end
subgraph consumer
direction TB
D([消费者1])
E([消费者1])
end
B[缓冲区]
A-->B
C-->B
B-->D
B-->E
Caution
如果去除互斥信号量 mutex,在盘子空间为 1 的情况中,不会有影响;但如果盘子空间大于 1 时,生产者有可能会同时对 plate 进行访问(只要 plate > 0 即可访问),违背了互斥访问 的原则,数据有可能会被覆盖 。
9. 图书馆就坐
图书馆有 100 个座位,一张座位表。每个人必须在登记座位表后,才能对号入座。在离开图书馆时,需要从座位表注销。
semaphore seat = 100 , mutex = 1 ;