生产者―消费者问题算法的实现

实验目的

  • 掌握进程、线程的概念,熟悉相关的控制语;
  • 掌握进程、线程间的同步原理和方法;
  • 掌握进程、线程间的互斥原理和方法;
  • 掌握使用信号量原语解决进程、线程间互斥和同步方法。

实验思路

      生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。

      与此同时,消费者也在缓冲区消耗这些数据。

      该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

      为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有 n 个缓冲区的缓冲池:生产者进程从文件中读取一个数据,并将它存放到一个缓冲区中;消费者进程从一个缓冲区中取走数据,并输出此数据。生产者和消费者之间必须保持同步原则:不允许消费者进程到一个空缓冲区去取产品;也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区中投放产品。

      实现一个生产者对多个消费者,每一次生成数随机,若为奇数为生产者生产,反之为消费者消费,因此过程中会有消费者多次消费,是1对N的。

实验过程与内容
 

1设置一个生产者、消费者类,成员为以下

	int in,out;//指向有产品的下标的指针
	int count;//缓冲区的产品数
	int buffer[N];//缓冲区,其中N为缓冲区的大小
	int mutex;//互斥信号量
	int  empty,full;//判断缓冲区是否为空或者满,信号量

2实现PV操作

void Wait(int &s){//P操作
		if(s<=0) return ;
		s--;
	}

void Signal(int &s){//V操作
		s++;
	}

3生产者生产

void Producer(){
		if(empty==0){
			full=1;
			cout<<"产品已满,不能再生产"<<endl;
			return ;
		}
		Wait(empty);//empty--;

		buffer[out]=1;	//生产一个产品
		out=(out+1)%N;	//尾指针后移一位,构成循环队列

		cout<<"生产者生产了一个产品"<<endl;

		/*对count互斥访问*/
		Wait(mutex);
		count++;
		Signal(mutex);
	}

4消费者消费

	void Consumer(){
		if(empty==N){
			cout<<"没有产品,不能消费"<<endl;//每次消费之前判断当前缓冲池有无商品
			return ;
		}

		buffer[in]=0;//消费一个产品
		in=(in+1)%N;//头指针后移一位,构成循环队列

		cout<<"消费者消费了一个产品"<<endl;

		/*对count互斥访问*/
		Wait(mutex);
		count--;
		Signal(mutex);

		if(empty<=N){
			full=0;
			Signal(empty);//empty++;
		}
	}

5显示缓冲区的情况

void Display(){
		for(int i=0;i<N;i++)
			cout<<buffer[i];
		cout<<endl<<"一共有"<<count<<"个产品"<<endl<<endl;
	}

结果展示

为方便展示,缓冲区设置为10,只进行50次生产和消费,可以修改代码,增加缓冲区容量和增加生产者消费者的操作次数。

结论

缓冲区是临界资源,各进程必须互斥地访问,为了避免两个进程之间相互干扰,我们利用信号量机制来实现进程间互斥和同步 ,使用数组模拟的循环队列来完成缓冲区。

生产者进程向缓冲区投入产品,则count加一;消费者进程向缓冲区取出产品,则count减一

完整代码 

#include <iostream>//一个生产者,多个消费者,因为生成数随机,会有消费者多次消费
                    //会在每次消费之前判断当前缓冲池有无商品
#include <time.h>//生成数随机,消费者、生产者生产和消费随机
#include <stdlib.h>
#define N 10	//产品数量,可以改
using namespace std;

/*生产者消费者类*/
class PandC{
private:
	int in,out;//指向有产品的下标的指针
	int count;//缓冲区的产品数
	int buffer[N];//缓冲区,其中N为缓冲区的大小
	int mutex;//互斥信号量
	int  empty,full;//判断缓冲区是否为空或者满,信号量
public:
	/*初始化,构造生产者消费者*/
	PandC(){
		in=0;out=0;count=0;
		empty=N;full=0;mutex=1;
		for(int i=0;i<N;i++)
			buffer[i]=0;
	}

	/*生产者生产*/
	void Producer(){
		if(empty==0){
			full=1;
			cout<<"产品已满,不能再生产"<<endl;
			return ;
		}
		Wait(empty);//empty--;

		buffer[out]=1;	//生产一个产品
		out=(out+1)%N;	//尾指针后移一位,构成循环队列

		cout<<"生产者生产了一个产品"<<endl;

		/*对count互斥访问*/
		Wait(mutex);
		count++;
		Signal(mutex);
	}

	/*消费者消费*/
	void Consumer(){
		if(empty==N){
			cout<<"没有产品,不能消费"<<endl;//每次消费之前判断当前缓冲池有无商品
			return ;
		}

		buffer[in]=0;//消费一个产品
		in=(in+1)%N;//头指针后移一位,构成循环队列

		cout<<"消费者消费了一个产品"<<endl;

		/*对count互斥访问*/
		Wait(mutex);
		count--;
		Signal(mutex);

		if(empty<=N){
			full=0;
			Signal(empty);//empty++;
		}
	}

	/*显示现在缓冲区的情况*/
	void Display(){
		for(int i=0;i<N;i++)
			cout<<buffer[i];
		cout<<endl<<"一共有"<<count<<"个产品"<<endl<<endl;
	}

	void Wait(int &s){
		if(s<=0) return ;
		s--;
	}

	void Signal(int &s){
		s++;
	}

};

int main(){
	int i=0;
	srand(time(NULL));
	PandC x;

	/*随机执行生产者生产还是消费者消费*/
	cout<<"初始状态"<<endl;x.Display();
	do{
	if(rand()&1)//利用比例控制生产速度,如果为奇数,为生产者生产
		x.Producer();
	else//否则为消费者消费
		x.Consumer();
	x.Display();
	i++;
	}while(i!=50);//(方便展示,只实现50次,可以修改次数)

	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值