采用信号量机制实现消费者与生产者的线程同步_信号量概述

1.先讲进程同步与互斥

进程互斥(mutex):进入临界区的进程只能有一个,当然前提是这些进程有共同的临界区。

进程同步:有逻辑关联的进程先后执行,比如B只有等A执行完了才能执行,A没执行完进程B只能挂起。这实际上是一种约束,更是一种通信。需要同步的进程之间不一定有共享临界区。

1)禁用硬件中断和基于硬件的原子操作(testandset以及exchange)单纯实现了进程互斥;

2)基于软件(peterson算法以及面包店算法)肯定实现了互斥,同时实现了同步;

3)信号量可以但不一定实现互斥(不是说不能,一种情况是不存在共享临界区,谈不上互斥,另一种情况是允许共同进入临界区,比如读操作),肯定实现了同步。

2.信号量

1)信号量(sem)是一个整型数字,程序开始需要给它一个初值,这里我们假设初值为org,其意义我们在后面根据信号量类型分开讨论。

sem=new Semaphore(org);

2)信号量有两个原子操作:P操作和V操作,具体意义也要分信号量类型的情况

·P() : sem减1

·V() : sem加1

3.信号量类型

1)二进制信号量

此时信号量的org初值只能是0和1。

2)一般/计数信号量

此时信号量的org初值可以是任意非负数。显然,其包含二进制信号量。

以下仅谈初值:

初值org设为0:此时信号量的意义单独拿出来,是因为此时信号量开始时可以有一个特殊的用途和分析。此时二进制信号量可以用于实现调度约束。只有线程B用V()发出信号(sem从0变到1)时,线程A中P()后的代码才能执行。
这是一个极简的进程同步例子,不牵扯互斥。当然单独实现这种操作不一定用信号量(信号量本质上还是一个类),随便定义变量赋任何值都可以对没有共享临界区的进程实现相同功能,只是信号量为0时恰好可以实现这种极简同步罢了。除了这一点外和其他信号量没有什么不同。

d1efcc89f57ad613ab50fe9fd44332cd.png

初值org设为1:此时信号量的值和一般信号量的值意义相同,即有多少个进程被允许访问临界区:同时只允许一个进程访问临界区——很明显的互斥概念。此时存在临界区,并且此时信号量就是一把“互斥锁”,和屏蔽中断、TestAndSet原子操作、peterson算法实现的功能完全一样,和原子操作尤其类似,因为PV本身也是原子操作(集判断能否进入和改变标志位为一身,不可被打断)。
但信号量自有的挂起,唤醒机制使其不存在忙等状态。

2a762b4bd8cdd1a81f04bdcfdcf2bf5d.png

cce981df8115efc4b7733d951a28572b.png

其他非负初值。表示有多少个进程可进入临界区,或者一个区域内有多少个资源。

4.信号量意义

PV过程中信号量大于0和小于0时,其代表的意义是不同的,这正是信号量的精妙所在。

当信号量≥0时,其表示还有sem个进程被允许进入临界区(某区域中还有sem个资源可以被获得),此时在临界区中的进程数目为org-sem(被取走的资源数目)。

当信号量<0时,表示有 |信号量| 个进程之前请求进入但不被允许后挂起的进程数目(想获得资源但目前给不了的进程数目),此时临界区中的进程数目为org(被拿走的资源数目),总的已经进入和即将进入的进程数目为org+|sem|(资源的总需求数目)。

允许多个线程进入的临界区限流?和缓存区资源的获取是信号量应用的两个主要方面。

5.例子:有界缓冲区的生产者消费者问题

30e69e4a8972907521089de8aa833c4a.png

1ae5631d3d35c0a3ce33dd395dc1e9a5.png

lock只能完成互斥要求,难以完成同步约束。消费者取不到东西会形成自旋,此时使用信号量主要使用其调度功能(何时挂起?何时唤醒?)。

d6ea8af354b7a8a763cc80ff1b7e89eb.png

V操作不会引起阻塞挂起,因此顺序可换,P操作会引起阻塞挂起,调换顺序可能引起死锁。

6.信号量实现

79a189c7ca105172753f93c65a7230aa.png

PV操作都是原子指令。

7.小结

信号量的双用途:互斥与同步

信号量的优点:无忙等

信号量这块本来就错综复杂,各种概念交织,总结成这样尽力了,就这样吧。。。。

参考:清华大学Chen Yu老师《操作系统原理》

生产者消费者问题算法实现》 设计思想 因为有多个缓冲区,所以生产者线程没有必要在生成新的数据之前等待最后一个数据被消费者线程处理完毕。同样,消费者线程并不一定每次只能处理一个数据。在多缓冲区机制下,线程之间不必互相等待形成死锁,因而提高了效率。   多个缓冲区就好像使用一条传送带替代托架,传送带上一次可以放多个产品。生产者在缓冲区尾加入数据,而消费者则在缓冲区头读取数据。当缓冲区满的时候,缓冲区就上锁并等待消费者线程读取数据;每一个生产或消费动作使得传送带向前移动一个单位,因而,消费者线程读取数据的顺序和数据产生顺序是相同的。 可以引入一个count计数器来表示已经被使用的缓冲区数量。用hNotEmptyEvent 和hNotFullEvent 来同步生产者消费者线程。每当生产者线程发现缓冲区满( count=BufferSize ),它就等待hNotEmptyEvent 事件。同样,当消费者线程发现缓冲区空,它就开始等待hNotEmptyEvent。生产者线程写入一个新的数据之后,就立刻发出hNotEmptyEvent 来唤醒正在等待的消费者线程;消费者线程在读取一个数据之后,就发出hNotFullEvent 来唤醒正在等待的生产者线程。 程序的设计思想大致为:设置一while循环,pi生产者访问临界区,得到权限访问缓冲区,如果缓冲区满的,则等待,直到缓冲区非满;访问互斥锁,当得到互斥锁且缓冲区非满时,跳出while循环,开始产生新数据,并把数据存放于Buffer缓冲区中,当数据存放结束则结束临界区;接着唤醒消费者线程;ci消费者访问临界区,得到权限访问缓冲区,如果缓冲区为空,没有可以处理的数据,则释放互斥锁且等待,直到缓冲区非空;当等到缓冲区非空时,跳出while循环;消费者获得数据,并根据所获得的数据按类别消费(当消费者获得的数据为大写字母时,则把大写字母转换成小写字母,并显示;当消费者获得的数据为小写字母时,则把小写字母转换成大写字母,并显示;当消费者获得的数据为字符0、1、2、……8、9时,把这些字符直接显示到屏幕;当消费者获得的数据为符号(+、-、*、\……)时,把这些符号打印成7行7列的菱形);处理完数据后,结束临界区;接着唤醒生产者线程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值