生产者消费者问题实现
班级姓名学号
一.实验目的
1.熟悉临界资源、信号量及PV操作的定义与物理意义
2.了解线程通信的方法
3.掌握线程互斥与同步的相关知识
4.掌握用信号量机制解决线程之间的同步与互斥问题
5.实现生产者-消费者问题,深刻理解线程同步问题
二、实验的硬件、软件平台
硬件:计算机
软件:操作系统win10
应用软件:Dev C++
三、实验原理
生产者-消费者问题是一个经典的线程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。
在同一个线程地址空间内执行的两个线程。
生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。
消费者线程从缓冲区中获得物品,然后释放缓冲区。
当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。
当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。
多个生产/消费者在有界缓冲上操作。
它利用N个字节的共享内存作为有界循环缓冲区,利用写一字符模拟放一个产品,利用读一字符模拟消费一个产品。
当缓冲区空时消费者应阻塞睡眠,而当缓冲区满时生产者应当阻塞睡眠。
一旦缓冲区中有空单元,生产者线程就向空单元中入写字符,并报告写的内容和位置。
一旦缓冲区中有未读过的字符,消费者线程就从该单元中读出字符,并报告读取位置。
生产者不能向同一单元中连续写两次以上相同的字符,消费者也不能从同一单元中连续读两次以上相同的字符。
在进行多线程编程时,由于资源共享和进程间合作而造成进程间相互制约,难免还要碰到两个问题,那就线程间的互斥与同步:线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。
当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。
线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。
线程间的同步方法大体可分为两类:用户模式和内核模式。
内核模式就是指
利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。
用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。
内核模式下的方法有:事件,信号量,互斥量。
在本实验中,线程之间要进行通信来操作同一缓冲区。
一般来说,线程间的通信可以划分为三种:1)使用全局变量实现线程间通信;2)使用消息实现线程间通信;3)使用CEvent类实现线程间通信。
四、实验内容及步骤
实验内容:模拟操作系统中进程同步和互斥。
实现经典同步问题:生产者—消费者,具体要求如下(可采用C++或者java)
(1)一个大小为10的缓冲区,初始状态为空。
(2)2个生产者,随机等待一段时间,往缓冲区中添加数据,若缓冲区已满,等
待消费者取走数据之后再添加,重复10次。
(3)2个消费者,随机等待一段时间,从缓冲区中读取数据,若缓冲区为空,等
待生产者添加数据之后再读取,重复10次。
(4)按要求输出。
实验步骤:
首先,使用一个互斥锁,意味着资源槽机制就不能使用了。
因为资源槽虽以用一个互斥锁完成,但是需要有额外的通信,如果使用管道通信,则管道也必须是互斥,这就不满足1个互斥锁的要求。
其次,要求生产者一直生产,这就否定了另外一种方法:消费者、生产者的位置均平等,消费者消费的时候生产者不能生产,生产者生产的时候消费者不能消费。
因此,就需要采用A要求,也就是循环链表的形式。
为了保证互斥要求,需要定义一个数据结构,这个数据结构包含两个指针,一个读一个写,同时有一个资源数目量,告诉生产者和消费者是否可以生产或者消费。
由于该数据结构很小,因而可以对此结构互斥访问。
同时,对于每组数据,都有一个标志位,表示此组数据是否被占用,生产者和消费者均可以先占用此位置然后完成相应的操作。
当消费者互斥访问此结构时,首先判断是否有数据可以取,如果没有,直接等待,若有数据可取,先更改标志位占用此数据,并将资源数目-1。
然后交出互斥,把数据拷贝到自己缓冲区内,清空数据。
当生产者访问时,首先判断有没有空位可以生产,如果没有,直接等待,若有数据可以生产,先判断该位是否被占用,如果没被占用,则占用此位置进行生产。
生产完成后,将占用位改为未占用,同时将资源数目+1。
流程图: A 、主函数
Y
Y N
Y N
B 、统计线程
Y
N
Main 函数开始 初始化公共数据区
初始化信号量 创建生产者进程 创建消费者进程
创建统计进程 成功?
成功?
成功?
定时到 退出
开始进程 定时时间+1 满屏 打印消费结束进程
修改退出
C 、生产者线程 N
Y
Y
N
Y
N
N
Y
Y
开始进程
要求退出
结束进程
加锁成功
有空位
取得成功
加锁成功
生产数据
解锁
设置取得成功
失败计数+1
设置取得失败
检查资源数目
要求加锁
解锁
增加资源数目
要求加锁
D 、消费者线程
N
Y
N
N
N
N
Y
Y
五、实现代码
六、运行结果
开始进程
要求退出
结束进程
加锁成功
有产品
取得成功 加锁成功
消费数据
解锁
设置取得成功
失败计数+1
设置取得失败
检查资源数目
要求加锁
解锁
增加资源数目
要求加锁
七、实验心得
通过这次实验,我对linux的线程、进程等知识有了更直观深入的了解。
通过写这几个程序,弄清楚了信号量及互斥在使用中的具体运行方法。
除了涉及多个知识点之外,自己在做程序的时候也通过解决问题,对linux下一些函数认识的更加透彻,例如对进程间的通信,多线程的安全问题,管道问题都有了进一步的了解。