经典的进程同步问题
1、信号量
信号量机制是一种功能较强大的机制,可用来解决互斥和同步问题,它只能被两个标准的原语 wait(S) 和 signal(S) 访问,也可记为 “P 操作” 和 ”V 操作“。
1.1、整型信号量
整型信号量被定义为一个用来表示资源数目的整型量 S,wait 和 signal 操作可描述为:
wait(S){
while(S<=0);
S = S-1;
}
signal(S){
S = S+1;
}
1.2、记录型信号量
typedef struct{
int value;
struct process *L;
}semaphore;
wait 和 signal 操作可描述为:
void wait(semaphore S){
//相当于申请资源
S.value--;
if(S.value < 0){
插入等待队列;
block(S.L);
}
}
void signal(semaphore S){
//相当于释放资源
S.value++;
if(S.value <= 0){
从队列中唤醒一个进程;
wakeup(P);
}
}
2、经典的同步问题
2.1、生产者-消费者问题
2.1.1、生产者-消费者问题
2.1.2、多生产者-多消费者问题
问题描述:
桌子上有一个盘子,每次只能向其中放入一个水果。爸爸专门向盘子中放苹果,妈妈专门向盘子中放橘子,儿子专门等吃盘子中的橘子,女儿专门等吃盘子中的苹果。只有盘子为空时,爸爸或妈妈才能向盘子中放一个水果;仅当盘子中有自己吃的水果时,女儿或儿子可以从盘子中取出。
伪代码:
semaphore plate = 1,apple = 0,orange = 0;
dad(){
//父亲进程
while(1){
prepare an apple;
P(plate); //互斥向盘中取、放水果
put the apple on the plate;
V(apple); //允许取苹果
}
}
mom(){
//母亲进程
while(1){
prepare an orange;
P(plate); //互斥向盘中取、放水果
put the orange on the plate;
V(porange); //允许取橘子
}
}
daughter(){
//女儿进程
while(1){
P(apple); //互斥从盘中取苹果
take an apple from the plate;
V(plate); //允许向盘中放水果
eat the apple
}
}
son(){
//儿子进程
while(1){
P(orange); //互斥从盘中取橘子
take an orange from the plate;
V(plate); //允许向盘中放水果
eat the orange;
}
}
Python 语言实现:
from threading import Thread,Semaphore
from queue import Queue
from random import randint
from time import sleep
class MyThread(Thread): #创建进程类
def __init__(self,func,args,name=''):
Thread.__init__(self)
self.name = name
self.func = func
self.args = args
def run(self):
self.func(*self.args)
plate = Semaphore(1) #盘子信号量
orange = Semaphore(0) #橘子信号量
apple = Semaphore(0) #苹果信号量
q = Queue(1) #共享存储
def dad(queue):
while(True):
plate.acquire() #互斥向盘子中放水果
queue.put('apple')
print("爸爸准备一个苹果...")
apple.release() #释放盘子,允许取苹果
sleep(randint(2,5))
def mom(queue):
while (True):
plate.acquire() #互斥向盘子中放水果
queue.put('orange')
print('妈妈准备了一个橘子...')
orange.release() #允许取橘子
sleep(randint(2,5))
def son(queue):
while(True):
orange.acquire() #取橘子
queue.get()
print('儿子拿走了一个橘子')
plate.release() #盘子为空
sleep(randint(1, 3))
def daughter(queue):
while(True):
apple.acquire() #取苹果
queue.get()
print('女儿拿走了苹果')
plate.release() #盘子为空
sleep(randint(1, 3))
funs = [dad,mom,son,daughter]
threads = []
for fun in funs:
t = MyThread(fun,(q,),fun.__name__)
threads.append(t)
for i in range(len(funs)):
threads[i].start()
爸爸准备一个苹果...
女儿拿走了苹果
妈妈准备了一个橘子...
儿子拿走了一个橘子
妈妈准备了一个橘子...
儿子拿走了一个橘子
爸爸准备一个苹果...
女儿拿走了苹果
妈妈准备了一个橘子...
儿子拿走了一个橘子
爸爸准备一个苹果...
女儿拿走了苹果
...
2.2、读者-写者问题
问题描述:
有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会发生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:(1)允许多个读者可以同时对文件执行读操作;(2)只允许一个写者往文件中信息;(3)任一写者在完成写操作之前不允许其他读者或者写者工作;(4)写者执行写操作前,应让所有读者和写者全部退出。
伪代码:
int count = 0; //用于记录当前的读者数量
semaphore mutex = 1; //用于保护更新 count 变量时的互斥
semaphore rw = 1; //用于保证读者和写者互斥地访问文件
semaphore w = 1; //用于实现 “写优先”
writer(){
while(1){
P(w); //在无写进程请求时进入
P(rw); //互斥地访问共享文件
writing;
V(rw); //释放共享文件
V(w); //恢复对共享文件的访问
}
}
reader(){
while(1){
P(w); //在无写进程请求时进入
P(mutex); //互斥地访问 count 变量
if(count == 0){
//当第一个读进程共享文件时
P(rw); //阻止写进程写
}
count++; //读者计数器加 1
V(mutex); //释放互斥变量
V(w); //恢复对共享文件的访问
reading;
P(mutex); //互斥访问 count 变量
count--;
if(count == 0){
//当最后一个进程读完共享文件
V(rw); //允许写进程写
}
V(mutex); //释放互斥变量 count
}
}
Python 语言实现:
import MyThread #把实现进程类作为模块导入
from threading import Semaphore, Lock
from time import sleep
from random import randint
count = 0
lock = Lock() #互斥访问全局变量直接使用锁
rw = Semaphore(1)
w = Semaphore(1)
def writer():
while (True):
w.acquire()
rw.acquire()
print('写入文件')
rw.release()
w.release()
sleep(randint(2, 4))
def reader():
global count
while (True):
w.acquire()
lock.acquire()
if count == 0:
rw.acquire()
count += 1
lock.release()
w.release()
print('目前有:', count, '个读者正在读文件')
sleep(randint(3, 5))
lock.acquire()
count -= 1
print('有一位读者退出,目前还有:', count, '个读者')
if count == 0:
rw.release()
lock.release()
sleep(randint(1, 3))
for i in range(randint(2,5)):
MyThread.MyThread(reader,(),reader.__name__).start()
for i in range(randint(1,3)):
MyThread.MyThread(writer,(),writer.__name__).start()
目前有: 1 个读者正在读文件
目前有: 2 个读者正在读文件
有一位读者退出,目前还有: 1 个读者
有一位读者退出,目前还有: 0 个读者
写入文件
目前有: 1 个读者正在读文件
目前有: 2 个读者正在读文件
有一位读者退出,目前还有: 1 个读者
有一位读者退出,目前还有: 0 个读者
写入文件
...
2.3、哲学家进餐问题
问题描述:
一张圆桌上坐着 5 名哲学家,每两名哲学家之间的桌子上摆着一根筷子,两根筷子之间是一碗米饭。哲学家倾注毕生精力思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根的拿起)。若筷子已在他人手上,则需要等待。饥饿的哲学家只有同时拿到了两根筷子才可以进餐,进餐完毕后,放下筷子继续思考。
伪代码:
semaphore chopstick[5] = {
1,1,1,1,1}; //初始化信号量
semaphore mutex = 1; //取筷子的信号量
Pi(){
do{
P(mutex); //在取筷子前获得信号量
P(chopstick[i]); //取左边筷子
P(chopstick[(i+1)%5); //取右边筷子
V(mutex); //释放取筷子的信号量
eat;
V(chopstick[i]); //放回左边筷子
V(chopstick[(i+1)%5); //放回右边筷子
think;
}
while(1);
}
Python 语言实现:
import MyThread
from threading import Lock,Semaphore
from time import sleep
from random import randint
chopstick1 = Semaphore(1)
chopstick2 = Semaphore(1)
chopstick3 = Semaphore(1)
chopstick4 = Semaphore(1)
chopstick5 = Semaphore(1)
lock = Lock()
def P1():
while(True):
lock.acquire()
chopstick1.acquire()
chopstick2.acquire()
lock.release()
print('第1号哲学家正在进餐')
sleep(randint(1, 3))
chopstick1.release()
chopstick2.release()
print('第1号哲学家吃完了饭,继续思考...')
def P2():
while(True):
lock.acquire()
chopstick2.acquire()
chopstick3.acquire()
lock.release()
print('第2号哲学家正在进餐')
sleep(randint(1, 3))
chopstick2.release()
chopstick3.release()
print('第2号哲学家吃完了饭,继续思考...')
def P3():
while(True):
lock.acquire()
chopstick3.acquire()
chopstick4.acquire()
lock.release()
print('第3号哲学家正在进餐')
sleep(randint(1, 3))
chopstick3.release()
chopstick4.release()
print('第3号哲学家吃完了饭,继续思考...')
def P4():
while(True):
lock.acquire()
chopstick4.acquire()
chopstick5.acquire()
lock.release()
print('第4号哲学家正在进餐')
sleep(randint(1, 3))
chopstick4.release()
chopstick5.release()
print('第4号哲学家吃完了饭,继续思考...')
def P5():
while(True):
lock.acquire()
chopstick5.acquire()
chopstick1.acquire()
lock.release()
print('第5号哲学家正在进餐')
sleep(randint(1, 3))
chopstick5.release()
chopstick1.release()
print('第5号哲学家吃完了饭,继续思考...')
MyThread.MyThread(P1,(),P1.__name__).start()
MyThread.MyThread(P3,(),P3.__name__).start()
MyThread.MyThread(P2,(),P2.__name__).start()
MyThread.MyThread(P5,(),P5.__name__).start()
MyThread.MyThread(P4,(),P4.__name__).start()
不知道为什么列表定义 chopstick = [Semaphore(1)]*5,无法运行。待解决。
第1号哲学家正在进餐
第3号哲学家正在进餐 #1 号进餐时 3 号可以进餐,并不冲突
第3号哲学家吃完了饭,继续思考...
第1号哲学家吃完了饭,继续思考...
第2号哲学家正在进餐
第5号哲学家正在进餐
第2号哲学家吃完了饭,继续思考...
第5号哲学家吃完了饭,继续思考...
第4号哲学家正在进餐
第4号哲学家吃完了饭,继续思考...
第3号哲学家正在进餐
第1号哲学家正在进餐
第1号哲学家吃完了饭,继续思考...
第3号哲学家吃完了饭,继续思考...
第2号哲学家正在进餐
第5号哲学家正在进餐
...
2.4、吸烟者问题
问题描述:
假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但要卷起并抽掉一支烟,抽烟者需要三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草,第二个拥有纸,第三个拥有胶水。供应商无限地提供三种材料,供应商每次将两种材料放到桌子上,拥有剩余那种材料的抽烟者卷起一根烟并抽掉它,并给供应者一个信号量告诉以完成,此时供应这会将另外两种材料放到桌子上,如此重复。
伪代码:
int = random; //存储随机数
semaphore offer1 = 0; //定义信号量对应烟草和纸的组合
semaphore offer2 = 0; //定义信号量对应烟草和胶水的组合
semaphore offer3 = 0; //定义信号量对应纸和胶水的组合
semaphore finish = 0; //定义信号量表示抽烟完成
process P1(){
while(1){
random = 随机数;
random = random % 3;
if(random == 0)
V(offer1); //提供烟草和纸
else if(random == 1)
V(offer2); //提供烟草和胶水
else
V(offer3); //提供纸和烟草
任意两种材料放在桌子上;
P(finish);
}
}
process P2(){ //拥有烟草者
while(1){
P(offer3);
拿纸和胶水,卷烟,抽掉;
V(finish);
}
}
process P2(){ //拥有纸者
while(1){
P(offer2);
拿烟草和胶水,卷烟,抽掉;
V(finish);
}
}
process P2(){ //拥有胶水者
while(1){
P(offer1);
拿烟草和纸,卷烟,抽掉;
V(finish);
}
}
Python 语言实现:
import MyThread
from threading import Semaphore
from time import sleep
from random import randint
offer1 = Semaphore(0)
offer2 = Semaphore(0)
offer3 = Semaphore(0)
finish = Semaphore(0)
def supplier():
while (True):
random = randint(1,3)
if random == 1:
print('【供应商提供了:烟草和纸】')
offer1.release()
elif random == 2:
print('【供应商提供了:烟草和胶水】')
offer2.release()
else:
print('【供应商提供了:纸和胶水】')
offer3.release()
finish.acquire()
def smoking1():
while(True):
offer3.acquire()
print('*****我是1号吸烟者,我拥有烟草***')
print('拿到了纸和胶水,开始吸烟')
sleep(randint(2, 4))
finish.release()
def smoking2():
while(True):
offer2.acquire()
print('*****我是2号吸烟者,我拥有纸***')
print('拿到了烟草和胶水,开始吸烟')
sleep(randint(2, 4))
finish.release()
def smoking3():
while(True):
offer1.acquire()
print('*****我是3号吸烟者,我拥有胶水***')
print('拿到了烟草和纸,开始吸烟')
sleep(randint(2, 4))
finish.release()
func = [supplier,smoking1,smoking2,smoking3]
for fun in func:
MyThread.MyThread(fun,(),fun.__name__).start()
【供应商提供了:烟草和纸】
*****我是3号吸烟者,我拥有胶水***
拿到了烟草和纸,开始吸烟
【供应商提供了:烟草和胶水】
*****我是2号吸烟者,我拥有纸***
拿到了烟草和胶水,开始吸烟
【供应商提供了:烟草和纸】
*****我是3号吸烟者,我拥有胶水***
拿到了烟草和纸,开始吸烟
【供应商提供了:纸和胶水】
*****我是1号吸烟者,我拥有烟草***
拿到了纸和胶水,开始吸烟
...