文章目录:
一、生产者消费者模型
(一)问题描述
【1. 问题描述:】
生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。 如下图所示:
【2. 关键点:】
- 消费者和生产者必须互斥的使用缓冲区。
- 缓冲区空时,消费者不能读取数据。
- 缓冲区满时,生产者不能添加数据。
- 消费者和生产者是一种同步关系,即:在生产者生产时,消费者不能消费;消费者消费时,生产者不能生产;只有生产者生产之后,消费者才能消费。
(二)问题分析
首先我们要明确一个点:我们需要循环从缓冲区放/取数据,缓冲区类似一个循环数组,即:
- 生产者往数组中放数据,(i+1)%N的放,这样数组不会出现溢出情况。
- 消费者从数组中取数据,(j+1)%N的取,这样不会出现数组越界。
我们假设缓冲区现在大小为5,如果我们不进行取余运算,那么生产者取数据时,j=6就会越界,但此时缓冲区中还有数据在1号为放着,所以应该取余6%5=1,到1号位去取数据。
其次,我们根据关键点确定我们需要控制三件事情:
- 缓冲区互斥访问,我们可以用互斥锁,信号量来控制这两个线程对临界区的访问,因为只有1块临界资源,所以初始值为1。
- 缓冲区所有格子放满了时,生产者不能生产。我们可以记录当前缓冲区没放数据的格子数,即空格子数,那么每次生产者往缓冲区放数据时,空格子数量-1,消费者取数据时,数量+1。当空格子数==0时, 生产者-1会阻塞。故我们可以用信号量来表示缓冲区为空状态的控制,初始值为N,还没有往里面放数据,生产P(-1),消费(V+1),当P(-1)操作阻塞,表示缓冲区的所有格子都放满了。
- 缓冲区中没有数据时,消费者不能消费,同理也用信号量来控制,我们记录当前放数据的格子的个数,初始值为0, 表示此时没有数据,生产者生产+1,消费者消费-1,当==0时,消费者阻塞。
假设我们定义三个控制变量分别为:mutex缓冲区信号量;empty缓冲区空格子数;full缓冲区填充数据的格子数。缓冲区用数组表示,大小为5,假设现在已经放了两个数据,那么有下图:
最后我们构思生产者消费者的整体流程,都是循环的读,写数据的,buff数组为临界资源。
生产者:
- P(empty) 操作,获取空格子数,此时empty-1,P(mutex)获取临界资源访问权,如果成功mutex=0,失败表示消费者正在读,阻塞mutex=-1。
- 给buff[i]放入随机数,i=(i+1)%N,下一个放数据的位置。
- V(full),full+1表示有数据格子数增加,V(mutex) 释放临界资源
消费者:
- P(full) 操作,读取数据,此时full-1,表示有数据格子数减一;P(mutex) 获取临界资源访问权,mutex=0。
- 从buff[j]中取数据,j=(j+1)%N
- V(empty),表示空格子数加一,V(mutex) 释放临界资源
(三)代码实现
我们开辟2个生产者,2个消费者,对缓冲区大小为5的buff进行写数据,取数据。
# include<stdio.h>
# include<assert.h>
# include<unistd.h>
# include<stdlib.h>
# include<pthread.h>
# include<semaphore.h>
# include<string.h>
int buff[5]={
0};//缓冲区
sem_t mutex;//控制缓冲区
sem_t empty;//控制空格子数
sem_t full;//控制有数据格子
int i=0;
int j=0;
void* producer(void* argc)
{
while(1)
{
int index=(int)(argc);
srand(time(NULL));//生成随机数字
int data=rand()%11;
sem_wait(&empty);
sem_wait(&mutex);
buff[i]=data;
printf("生产者线程%d往buff%d下标添加数字%d\n",index,i,data);
i=(i+1)%5;
sem_post(&full);
sem_post(&mutex);
sleep(1);
}
}
void* consumer(void* argc)
{
while(1)
{
int index=(int)(argc);
srand(time(NULL));
int data=rand()%11;
sem_wait(&full);
sem_wait(&mutex);
printf("消费者线程%d从buff第%d取出数字%d\n",index,j,buff[j]);
buff[j]=0;
j=(j+1)%5;
sem_post(&empty);
sem_post(&mutex);
sleep(1);
}
}
int main()
{
pthread_t id[4];
sem_init(&mutex,