之前介绍的几种解决进程间互斥的方案,不管是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. 【Flask】Sqlalchemy lazy

    ### 懒加载:在一对多,或者多对多的时候,如果想要获取多的这一部分的数据的时候,往往能通过一个属性就可以全部获取了.比如有一个作者,想要或者这个作者的所有文章,那么可以通过user.articles ...

  2. Python编程-函数进阶二

    一.生成器补充 1.什么是生成器? 可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象. 2.生成器分类 (1) ...

  3. Linux文件系统及文件储存方式【转】

    本文转载自:https://blog.csdn.net/qyp199312/article/details/54927843 前言 Linux文件系统构成 文件式的文件结构 Linux的一个具体文件 ...

  4. ubuntu 的mysql 安装过程和无法远程的解决方案

    ubuntu 的mysql 安装过程和无法远程的解决方案 安装完mysql-server启动mysqlroot@ubuntu:# /etc/init.d/mysql start (如果这个命令不可以, ...

  5. 实现利用公钥私钥免密码登录Linux服务器

    原理 客户端生成公钥私钥,把公钥拷贝给linux服务器,用自己的私钥连接服务器.实现如下: 如果是两台Linux服用器A和B,A来实现免密码登录B A执行ssh-keygen -t rsa 就会在/r ...

  6. SpringBoot2.0之整合Kafka

    maven依赖: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www. ...

  7. HTTP 指纹识别v0.1

    // Winhttp.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <Windows.h> #include ...

  8. git终端配置颜色

    默认情况下git是黑白的. git config --global color.status auto git config --global color.diff auto git config - ...

  9. demo(幸福大转盘)总结

    百度推广首页demo 在<head>与</head>之间加入代码 <link rel="shortcut icon" href="favic ...

  10. write.table函数语法:

    write.table (x,  file ="",  sep ="",  row.names =TRUE,  col.names =TRUE,  quote ...