经典进程同步问题(一)——生产者消费者问题

目录

一、生产者消费者问题描述

二、解决思路

三、问题求解:

四、源码

五、运行结果: 


一、生产者消费者问题描述

        生产者消费者(producer—customer)问题是一个非常著名的进程同步问题。它描述的是:生产者进程在生产“产品”,消费者进程则消费这些“产品”,在生产者进程和消费者进程之间存在一个缓冲池(生产者将生产的产品放入该缓冲池,消费者则消费该缓冲池中的产品)。所有生产者进程、消费者进程都是相互独立的(即以异步的方式运行),但他们之间同时必须保持同步(即不允许消费者进程到空缓冲区取产品,也不允许生产者进程向满缓冲区存入产品)。

二、解决思路

        我们使用队列(先进先出表)Queue来表示上述具有n个缓冲区的缓冲池。每投入(取出)一个产品,缓存池中就相应的插入(删除)一个节点。由于该缓冲池是被组织成队列的形式,因此,队空队满的判断条件分别如下:

q->front == q->rear;    //队空
q->front == (q->rear+1)%SIZE;    //队满

        此外,我们引入一个整型变量num,置其初值为0,当生产者(消费者)进程向缓冲池投入(取走)一个产品时,num对应的加一(减一)。

        同时,由于缓冲区是共享的,因此需要对生产者、消费者使用缓冲区进行限制,以此达到同步的效果,即在生产者向缓冲区投入产品时,消费者不得使用缓冲池;消费者向缓冲区取出产品时同理。

三、问题求解

通过以上的分析,我们可以得到一个解决思路如下:

void producer()
{
    sem_wait();
    lockf();
    QueueFull();
    Enqueue();
    unlockf();
    sem_post();
}
void customer()
{
    sem_wait();
    lockf();
    QueueEmpty();
    Dequeue();
    unlockf();
    sem_post();
}

  这段伪代码的思路已经十分明确了。

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

·为避免生产者进程和消费者进程同时使用资源,我们采用上锁的方式来达到独占资源的目标

·为了避免两个进程之间相互干扰,我们利用信号量机制来实现进程间互斥和同步   

但是,仔细思考就会发现,仅仅只是这样是远远不够的。我们还必须保证队满不入、队空不取。因此,在上述结构的基础上,我们加入条件判断机制。

四、源码

由于容器Queue的容量为10,因此我只运行生产者、消费者各十次,且出于简单设计的角度,该程序仅仅展示了单生产者-单消费者的情况。其他的情况,如果后面有空余时间,我会尝试一下的。

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
 
#define TRUE  1
#define FALSE 0
#define SIZE 11
 
typedef int QueueData;	//定义一个整型变量QueueData,与为基本数据类型定义新的别名方法一样 

typedef struct _queue   //队列结构体
{
	int data[SIZE];
	int front;     // 指向队头的下标
	int rear;      // 指向队尾的下标
}Queue;
 
struct data             //信号量结构体
{
	sem_t count;
	Queue q;
};

struct data sem;
pthread_mutex_t mutex;	//互斥变量使用特定的数据类型 
int num = 0; 

int InitQueue (Queue *q)   // 队列初始化
{
	if (q == NULL)
	{
		return FALSE;
	}
	q->front = 0;
	q->rear  = 0;
	return TRUE;
}
 
int QueueEmpty (Queue *q)      //判断空队情况
{
	if (q == NULL)
	{
		return FALSE;
	}
	return q->front == q->rear;
}
 
int QueueFull (Queue *q)     //判断队满的情况
{
	if (q == NULL)
	{
		return FALSE;
	}
	return q->front == (q->rear+1)%SIZE;
} 
 
int DeQueue (Queue *q, int x)   //出队函数
{
	if (q == NULL)
	{
		return FALSE;
	}
	if (QueueEmpty(q))
	{
		return FALSE;
	}
	q->front = (q->front + 1) % SIZE;
	x = q->data[q->front];
	return TRUE;
}
 
int EnQueue (Queue *q, int x)   //进队函数
{
	if (q == NULL)
	{
		return FALSE;
	}	
	if (QueueFull(q))
	{
		return FALSE;
	}	
	q->rear = (q->rear+1) % SIZE;
	q->data[q->rear] = x;
	return TRUE;
}
 
void *Producer()
{
	int i=0;
	while(i<10)
	{
		i++;
		int time = rand() % 10 + 1;          //随机使程序睡眠0点几秒           
		usleep(time * 100000);
		                                      
		sem_wait(&sem.count);                 //信号量的P操作(使信号量的值减一) 
		pthread_mutex_lock(&mutex);           //互斥锁上锁
		if(!QueueFull(&sem.q))					//若队未满 
		{
			num++;                                
			EnQueue (&sem.q, num);              //消息进队
			printf("生产了一条消息,count=%d\n", num); 
		}                                      
		else	printf("Full\n");                                       
		pthread_mutex_unlock(&mutex);         //互斥锁解锁
		sem_post(&sem.count);                  //信号量的V操作(使信号量的值加一) 
	}
	printf("i(producer)=%d\n",i);
}
 
void *Customer()
{
	int i=0;
	while(i<10)
	{
		i++;
		int time = rand() % 10 + 1;   //随机使程序睡眠0点几秒
		usleep(time * 100000);
		
		sem_wait(&sem.count);           //信号量的P操作
		pthread_mutex_lock(&mutex);    //互斥锁上锁
		
		if(!QueueEmpty(&sem.q))			//若队未空 
		{
			num--;
			DeQueue (&sem.q, num);       //消息出队
			printf("消费了一条消息,count=%d\n",num);
		}
		else	printf("Empty\n");
		pthread_mutex_unlock(&mutex);  //互斥锁解锁
		sem_post(&sem.count);         //信号量的V操作
	}
	printf("i(customer)=%d\n",i);
}
 
int main()
{
	srand((unsigned int)time(NULL));
	
	//信号量地址,信号量在线程间共享,信号量的初始值 
	sem_init(&sem.count, 0, 10);    //信号量初始化(做多容纳10条消息,容纳了10条生产者将不会生产消息)
	
	pthread_mutex_init(&mutex, NULL);  //互斥锁初始化
	
	InitQueue(&(sem.q));   //队列初始化
	
	pthread_t producid;
	pthread_t consumid;
	
	pthread_create(&producid, NULL, Producer, NULL);   //创建生产者线程
	pthread_create(&consumid, NULL, Customer, NULL);   //创建消费者线程
	
	pthread_join(consumid, NULL);    //线程等待,如果没有这一步,主程序会直接结束,导致线程也直接退出。
	
	sem_destroy(&sem.count);         //信号量的销毁 
	
	pthread_mutex_destroy(&mutex);   //互斥锁的销毁
	
	return 0;
}

五、运行结果: 

仔细分析可以认为达成实验目的。

猜你喜欢

转载自blog.csdn.net/aimat2020/article/details/121641563