生产者消费者模型的理解

生产者消费者模型的理解

生产者消费者模型也叫缓存绑定问题,是一个经典的、多进程同步问题。

单生产者和单消费者
  • 有两个进程:一组生产者进程和一组消费者进程共享一个初始为空、固定大小为的缓冲区。
  • 生产者:生产者的工作是制造一段数据,只有缓冲区没满时,生产者才能把消息放入到缓冲区,否则必须等待,如此反复;
  • 消费者:只有缓冲区不空时,消费者才能从缓冲区中取出消息,一次消费一段数据(即将其从缓存中移出),否则必须等待。
  • 由于缓冲区是临界资源,它只允许一个生产者放入消息,或者一个消费者从中取出消息。

  • 需要解决的主要问题

    1. 生产者在缓存还是满的时候不能向缓存区写数据;
    2. 消费者不能从空的缓存中取出数据。

    生产者和消费者对缓冲区互斥访问是互斥关系,同时生产者和消费者又是一个相互协作的关系,只有生产者生产之后,消费者才能消费,他们也是同步关系。

    解决思路:
    对于生产者,如果缓存是满的就去睡觉。消费者从缓存中取走数据后就叫醒生产者,让它再次将缓存填满。若消费者发现缓存是空的,就去睡觉了。下一轮中生产者将数据写入后就叫醒消费者。

    不完善的解决方案会造成“死锁”,即两个进程都在“睡觉”等着对方来“唤醒”。

    只有生产者和消费者两个进程,正好是这两个进程存在着互斥关系和同步关系。那么需要解决的是互斥和同步PV操作的位置。使用“进程间通信”,“信号标”semaphore就可以解决唤醒的问题:

    我们使用了两个信号标:full 和 empty 。信号量mutex作为互斥信号量,它用于控制互斥访问缓冲池,互斥信号量初值为1;信号量 full 用于记录当前缓冲池中“满”缓冲区数,初值为0。信号量 empty 用于记录当前缓冲池中“空”缓冲区数,初值为n。新的数据添加到缓存中后,full 在增加,而 empty 则减少。如果生产者试图在 empty 为0时减少其值,生产者就会被“催眠”。下一轮中有数据被消费掉时,empty就会增加,生产者就会被“唤醒”。

    该类问题要注意对缓冲区大小为n的处理,当缓冲区中有空时便可对empty变量执行P 操作,一旦取走一个产品便要执行V操作以释放空闲区。对empty和full变量的P操作必须放在对mutex的P操作之前。

    1、若生产者进程已经将缓冲区放满,消费者进程并没有取产品,即 empty = 0,当下次仍然是生产者进程运行时,它先执行 P(mutex)封锁信号量,再执行 P(empty)时将被阻塞,希望消费者取出产品后将其唤醒。轮到消费者进程运行时,它先执行 P(mutex),然而由于生产者进程已经封锁 mutex 信号量,消费者进程也会被阻塞,这样一来生产者进程与消费者进程都
    将阻塞,都指望对方唤醒自己,陷入了无休止的等待。
    2、若消费者进程已经将缓冲区取空,即 full = 0,下次如果还是消费者先运行,也会出现类似的死锁。
    不过生产者释放信号量时,mutex、full 先释放哪一个无所谓,消费者先释放 mutex 还是 empty 都可以。

多生产者多消费者
  • 在多个制造商和多个消费者出现的情况下就会造成拥护不堪的情况,会导致两个或多个进程同时向一个磁道写入或读出数据。要理解这种情况是如何出现的,我们可以借助于putItemIntoBuffer()函数。它包含两个动作:一个来判断是否有可用磁道,另一个则用来向其写入数据。如果进程可以由多个制造商并发执行,下面的情况则会出现:
    1. 两个制造商为emptyCount减值;
    2. 一个制造商判断缓存中有可用磁道;
    3. 第二个制造商与第一个制造商一样判断缓存中有可用磁道;
    4. 两个制造商同时向同一个磁道写入数据。

  • 多个生产者向一个缓冲区中存入数据,多个生产者从缓冲区中取数据。这是有界缓冲区问题,队列改写,生产者们之间、消费者们之间、生产者消费者之间互相互斥。共享缓冲区作为一个环绕缓冲区,存数据到尾时再从头开始。

    • 我们使用一个互斥量保护生产者向缓冲区中存入数据。由于有多个生产者,因此需要记住现在向缓冲区中存入的位置。

    • 使用一个互斥量保护缓冲区中消息的数目,这个生产的数据数目作为生产者和消费者沟通的桥梁。

    • 使用一个条件变量用于唤醒消费者。由于有多个消费者,同样消费者也需要记住每次取的位置。

  • 在选项中选择生产条目的数目,生产者的线程数目,消费者的线程数目。生产者将条目数目循环放入缓冲区中,消费者从缓冲区中循环取出并在屏幕上打印出来。

  • 为了克服这个问题,我们需要一个方法,以确保一次只有一个制造商在执行调用函数。换个说法来讲,我们需要一个有“互斥信号标”(mutal exclusion)的“关键扇区”(critical section)。为了实现这一点,我们使用一个叫mutex二位信号标。因为一个二位信号标的值只能是1或0,只有一个进程能执行down(mutex)或up(mutex)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_长风_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值