之前介绍的几种解决进程间互斥的方案,不管是Peterson方案还是TSL指令的方式,都有一个特点:当一个进程被Block到临界区外面时,被Block的进程会一直处于忙等待的状态,这个不但浪费了CPU资源,还会有一个很坏的副作用。假设两个进程,H,L,H的优先级高,L进程的优先级非常低,CPU的调度规则是只要H处于Ready状态,则开始运行H,问题来了:

  • H处于BLock状态,可能在等待外部资源
  • L进入了临界区
  • H处于Ready状态(外部资源满足),L还没有离开临界区
  • 按照调度规则,CPU会Switch到H,L会挂起。
  • H开始运行,但是由于L在临界区,这个时候H会处于无限循环,一直处于临界区外面等待。
  • L也将永远无法离开临界区

一、生产者消费者引入

1.1 解决无限等待问题

如果一个进程不容许进入临界区时,这个时候让这个进程block,则解决了无线等待问题,文章开头提出的问题也就解决了。怎么block进程。

  1. 通过系统调用sleep,则可以让进程处于block状态。
  2. 被sleep的进程会一直挂起直到被调用wakeup

示意图:

生产者消费者主要是当往Buffer插入数据的时候发现buffer已经满了,则让生产者sleep,而是不无限循环等待

当发现buffer没有数据的时候让消费者sleep,而不是无限循环等待。

当buffer有数据时候,但是发现之前消费者属于sleep状态,则让消费者wakeup。

当buffer没有数据时候,但是发现之前生产者处于sleep状态,则让生产者wakeup。

1.2 代码演示

#definne N   //buffer 的容量
int count = ; //buffer中item的数量 void producer(void)
{
int item;
while(true)//永远执行
{
item= produce_item();//生成下一个item
   if(count == N)//如果buffer已经满了,则sleep
{
sleep();
}
insert_item(item);//将item放入buffer
   count = count + ;//数量+1
   if(count == )//之前buffer是空的,所以consumer处于sleep状态,现在有item了,唤醒consumer
{
wakeup(consumer);
}
}
} void consumer(void)
{
int item;
while(true)
{
if(count == )//如果buffer是空的,则sleep
{
sleep();
}
item = remove_item();//将item从buffer移除
  count = count - ;//数量-1
  if(count == N - )//之前buffer已经满了,所以producer处于sleep状态,现在移除了一个item,唤醒producer
{
wakeup(producer);
}
consume_item(item);//消费item
}
}

二、生产者消费者的条件竞争问题

上面的代码依然存在竞争条件问题(Race Conditions),考虑如下情况:

  1. buffer是空的,消费者刚刚读取到count,要去判断count是不是0,还没有走到sleep
  2. scheduler决定暂时停止消费者,让CPU去运行生产者
  3. 生产者如程序所示,会在buffer中增加一个item
  4. 因为之前count是你0,所以消费者认为consumer应该是sleep状态
  5. 发送wakeup(consumer)指令

以上完成后,由于之前的消费者因为cpu的调度没有走到sleep状态,所以wakeup对消费者是不起作用的。所以wakeup的信号就丢失了。

继续:

  1. cpu决定运行消费者,这个时候会从之前暂停的地方开始,count==0成立,consumer会进入sleep状态
  2. 接下来当生产者开始运行的时候,因为count已经是1了,所以永远都不会在运行wakeup(消费者)

最终结果:

  1. 生产者把buffer插满
  2. 消费者永远处于sleep状态

上面问题的关键是wakeup指令发给了一个没有sleep的进程,wakeup不起作用,wakeup的型号最终丢失了。

一个快速的解决方案是给进程加一个Wakeup waiting Bit,当一个wakeup请求发给一个处于wakeup的进程时,将这个进程的Wakeup waiting Bit标志位设置为true。接下来当有sleep给这个进程时,判断一下如果Wakeup waiting Bit为true,则不sleep。

这个方法能暂时解决问题,但是很糟糕,如果有多个进程时,就需要很多标志位。接下来几篇文章会专门将信号量,互斥体等来解决生产者消费者的竞争条件问题。

Operating System-进程间互斥的问题-生产者&&消费者引入的更多相关文章

  1. Operating System-进程间互斥的方案-保证同一时间只有一个进程进入临界区(3)- TSL指令

    本文接上一篇文章继续介绍如何实现同一时间只允许一个进程进入临界区的机制.本文主要介绍TSL指令. 方案汇总 屏蔽中断 锁变量 严格轮换法 TSL指令 Peterson解法 一.What is TSL ...

  2. pthread mutex 进程间互斥锁实例

    共享标志 定义 名称 描述 0 PTHREAD_PROCESS_PRIVATE 进程内互斥锁 仅可当前进程内共享 1 PTHREAD_PROCESS_SHARED 进程间互斥锁 多个进程间共享 第一个 ...

  3. POSIX信号量与互斥锁实现生产者消费者模型

    posix信号量 Link with -lpthread. sem_t *sem_open(const char *name, int oflag);//打开POSIX信号量 sem_t *sem_o ...

  4. python并发编程之守护进程、互斥锁以及生产者和消费者模型

    一.守护进程 主进程创建守护进程 守护进程其实就是'子进程' 一.守护进程内无法在开启子进程,否则会报错二.进程之间代码是相互独立的,主进程代码运行完毕,守护进程也会随机结束 守护进程简单实例: fr ...

  5. Mutex 进程间互斥

    学习Mutex的心得,不一定对,先记录一下. 同步技术分为两大类,锁定和信号同步. 锁定分为:Lock.Monitor 信号同步分为:AutoResetEvent.ManualResetEvent.S ...

  6. Linux多线程实践(5) --Posix信号量与互斥量解决生产者消费者问题

    Posix信号量 Posix 信号量 有名信号量 无名信号量 sem_open sem_init sem_close sem_destroy sem_unlink sem_wait sem_post ...

  7. posix 匿名信号量与互斥锁 示例生产者--消费者问题

    一.posix 信号量 信号量的概念参见这里.前面也讲过system v 信号量,现在来说说posix 信号量. system v 信号量只能用于进程间同步,而posix 信号量除了可以进程间同步,还 ...

  8. 进程同步控制(锁,信号量,事件), 进程通讯(队列和管道,生产者消费者模型) 数据共享(进程池和mutiprocess.Pool模块)

    参考博客 https://www.cnblogs.com/xiao987334176/p/9025072.html#autoid-1-1-0 进程同步(multiprocess.Lock.Semaph ...

  9. 网络编程基础----并发编程 ---守护进程----同步锁 lock-----IPC机制----生产者消费者模型

    1  守护进程: 主进程 创建 守护进程   辅助主进程的运行 设置进程的 daemon属性 p1.daemon=True 1 守护进程会在主进程代码执行结束后就终止: 2 守护进程内无法再开启子进程 ...

随机推荐

  1. linux命令详解之df(6/19)

    df命令作用是列出文件系统的整体磁盘空间使用情况.可以用来查看磁盘已被使用多少空间和还剩余多少空间. df命令显示系统中包含每个文件名参数的磁盘使用情况,如果没有文件名参数,则显示所有当前已挂载文件系 ...

  2. Android驱动开发全过程(有图有真相)

    转:https://wenku.baidu.com/view/349bd159b7360b4c2e3f64b1.html

  3. Linux 安装扩展yum源

    Linux 安装扩展yum源 下载rpm扩展:http://rpmfind.net/linux/epel/6/x86_64/epel-release-6-8.noarch.rpm CentOS/RHE ...

  4. POJO、Bean和JavaBean

    本文总结自: https://blog.csdn.net/chenchunlin526/article/details/69939337 POJO (plain pld java object) 一个 ...

  5. id和NSObject *和instanceType的区别与联系

    id 被成为万能指针,也就是可以指向任何对象. NSObject * 本身就是定义指向NSObject类型的指针. 那么这两者有什么区别吗? 这两者都是既可以作为返回值,又可以作为变量修饰.而其主要区 ...

  6. JSP 导出Excel表格

    ES6语法 传入一个table的id,然后在导出excel按钮上加入一个<a href="#" id="buttonId">导出Excel</ ...

  7. BZOJ-1396: 识别子串

    后缀自动机+线段树 先建出\(sam\),统计一遍每个点的\(right\)集合大小\(siz\),对于\(siz=1\)的点\(x\),他所代表的子串只会出现一次,设\(y=fa[x]\),则这个点 ...

  8. 【bzoj2118&洛谷P2371】墨墨的等式(最短路神仙题)

    题目传送门:bzoj2118 洛谷P2371 这道题看了题解后才会的..果然是国家集训队的神仙题,思维独特. 首先若方程$ \sum_{i=1}^{n}a_ix_i=k $有非负整数解,那么显然对于每 ...

  9. 用java.lang.Math.random()语句,随机输出{size:自定义参数}个数不重复并且按顺序从小到大排列(冒泡排序)

    package com.test; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lan ...

  10. SpringCloud-分布式配置中心(config)

    简介 在分布式文件系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件.在Spring Cloud中,有分布式配置中心组件spring cloud config ...