经典的进程同步问题详解

2.5 经典的进程同步问题(重点!!!)

  • 一、生产者-消费者问题
  • 二、哲学家进餐问题
  • 三、读者-写者问题

一、生产者-消费者问题

图片1

有几个进程?

存在什么模式?

对于生产者-消费者问题,存在两个进程,分别是生产者进程和消费者进程
buffer是生产者和消费者两个进程共享的公共资源,属于临界资源,所以是互斥方式共享buffer。
1、利用记录型信号量解决生产者-消费者问题

可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;
利用信号量empty和full分别表示缓冲池中空缓冲池和满缓冲池的数量

程序实现

图片2

对于producer程序而言,wait(empty)看缓冲区是否还有存储空间,wait(mutex)则是用来申请缓冲区资源
对于consumer程序而言,wait(full)看缓冲区中时候有商品,wait(mutex)则是用来申请缓冲区资源

图片3

当缓冲区是满的时候,empty为0,full为n。此时若先执行wait(mutex),再执行wait(empty),会造成生产者申请到缓冲区资源,但是缓冲区没有存储空间来存储商品;而对于消费者进程而言,若先执行wait(mutex),再执行wait(full),那么会导致,消费者申请不到缓冲区资源,此时即使缓冲区有商品,依然无法将商品取出消费。陷入死锁。
生产者在等消费者去消费,消费者在等生产者释放mutex。(死锁的情况)
退出区释放资源的时候没有顺序要求!
2、利用AND型信号量解决生产者-消费者问题

图片4

一次性的申请和释放资源型信号量和互斥信号量。

二、哲学家进餐问题

图片5

1、利用记录型信号量解决哲学家进餐问题

图片6

程序实现

图片7

该算法虽然可以保证相邻两人不会同时进餐,但是有可能引起死锁。
因为当五个人同时饿了的时候,大家都会拿起自己左手边的一个筷子,这时每个人只有一只筷子没法进餐,因此也没法释放资源,于是五个人只能等着饿死,陷入死锁。
解决方法:
  • 方法1:至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用完后释放出他用过的两只筷子,从而使更多的哲学家能够进餐。
  • 方法2:仅当哲学家的左右两支筷子均可用时,才允许他拿起筷子进餐。
  • 方法3:规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;偶数号哲学家则相反。
方法1:程序实现

图片8

筷子是互斥信号量组
饥饿的哲学家的人数为资源信号量
方法2:利用AND型信号量机制解决哲学家进餐问题
在哲学家进餐问题中,要求每个哲学家先获得两个临界资源(筷子)后方能进餐。本质上是AND同步问题。

图片9

方法3:程序实现:

图片10

三、读者-写者问题

图片11

1、利用记录型信号量解决读者-写者问题

图片12

  • 互斥信号量wmutex:实现reader与writer进程间在读或写时的互斥,整型变量readcount:表示正在读的进程数目;
  • 由于只要有一个reader进程在读,便不允许writer进程写。所以,仅当readcount=0,即无reader进程在读时,reader才需要执行wait操作。若wait(wmutex)操作成功,reader进程便可去读,相应地,做readcount+1操作。
  • 同理,仅当reader进程在执行了readcount减1操作后其值为0时,才需执行signal(wmutex)操作,以便让write进程写。
    互斥信号量rmutex:reader进程间互斥访问readcount。

图片13

对于reader进程:
  • 1、先申请readcount资源,即执行wait(rmutex)
  • 2、如果readcount=0,则需要再申请wmutex,即执行wait(wmutex),然后执行readcount+1,接着释放readcount资源,即执行signal(rmutex)。
  • 3、如果readcount!=0,则直接执行readcount+1,然后释放readcount资源,即执行signal(rmutex)。
  • 4、执行读操作
  • 5、申请readcount资源,执行readcount-1
  • 6、如果readcount=0,那么释放wmutex,即执行signal(wmutex),否则直接释放rmutex,即执行signal(rmutex)。
    写进程就是正常的申请wmutex资源,然后执行写操作,释放wmutex,即执行signal(wmutex)。
2、利用信号量集机制解决读者-写者问题

图片14

图片15

对于reader进程和writer进程:
  • 每来一个读进程,会是L-1,最多可以使RN个读进程来申请资源
  • swait(mx,1,0)语句起着开关的作用。只要无writer进程进入写,mx=1,reader进程就都可以进入读。但只要一旦有writer进程进入写时,mx=0,则任何reader进程就都无法进入读。
  • swait(mx, 1, 1; L, RN, 0)语句表示仅当既无writer进程在写(mx=1),又无reader进程在读(L=RN),writer进程才能进入临界区写。
  • 7
    点赞
  • 120
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
生产者-消费者问题是指在一个共享缓冲区中,生产者进程向缓冲区中放入数据,而消费者进程从缓冲区中取出数据。为了避免生产者进程在缓冲区已满时继续放入数据,或者消费者进程在缓冲区为空时继续取出数据,需要进行进程同步与互斥。 一种解决方案是使用信号量。定义两个信号量:empty和full,分别表示缓冲区空闲的空间和缓冲区已经存储的数据量。当生产者进程向缓冲区中放入数据时,需要先判断empty信号量的值是否大于0,如果是,则表示缓冲区还有空闲的空间,可以放入数据;否则,需要等待消费者进程取出数据,释放空间。当消费者进程从缓冲区中取出数据时,需要先判断full信号量的值是否大于0,如果是,则表示缓冲区中有数据可以取出;否则,需要等待生产者进程放入数据,增加数据量。 下面是一个Python实现的生产者-消费者问题的例子: ```python import threading import time # 定义缓冲区大小 BUFFER_SIZE = 10 # 定义信号量 empty = threading.Semaphore(BUFFER_SIZE) full = threading.Semaphore(0) # 定义缓冲区 buffer = [] # 定义生产者线程 class ProducerThread(threading.Thread): def run(self): global buffer while True: # 生产数据 item = time.time() # 获取empty信号量 empty.acquire() # 向缓冲区中放入数据 buffer.append(item) print("Produced:", item) # 释放full信号量 full.release() # 等待一段时间 time.sleep(1) # 定义消费者线程 class ConsumerThread(threading.Thread): def run(self): global buffer while True: # 获取full信号量 full.acquire() # 从缓冲区中取出数据 item = buffer.pop(0) print("Consumed:", item) # 释放empty信号量 empty.release() # 等待一段时间 time.sleep(2) # 创建生产者线程和消费者线程 producer_thread = ProducerThread() consumer_thread = ConsumerThread() # 启动线程 producer_thread.start() consumer_thread.start() ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

loveCC_orange

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值