文章目录
问题概述
生产者消费者共享缓冲区,生产者向缓冲区中放数据,消费者从缓冲取中取数据,当缓冲区中被放满时,生产者线程就必须进入挂起状态,直到消费者从缓冲中取走数据时,生产者才能继续向缓冲区中存放数据,同样当缓冲取中没有数据时,消费者线程就必须进入挂起休眠状态,直到生产者向缓冲区中放入数据时,消费者才能被唤醒继续从缓冲区中取走数据。
单生产者单消费者队列
方法一(互斥锁和条件变量)
思路概述
使用队列的方式来存储产品,创建两个线程,一个生产者线程用来生产产品,生产的产品用头插的方法插进队列,另一个是消费者线程用来消费产品,使用互斥锁的方法让线程串行方式进行,消费者当产品为零时,也就是head->next==NULL,使用信号量函数pthread_cond_wait(&cond,&lock)让线程进入阻塞,直到有产品产出时,使用函数 pthread_cond_signal(&cond);唤醒被阻塞的线程。生产者,当产品到达最大容量时,做法与上类似,先让生产者线程进入阻塞,消费者线程将产品消耗后,该线程再次被唤醒
代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
int i=0;//产品序号
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//互斥锁
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//互斥量
#define MAX 8
void err_thread(int ret,const char*str)//检查错误
{
if(ret!=0)
{
fprintf(stderr,"%s:%s\n",str,strerror(ret));
exit(1);
}
}
typedef struct SPSCQueue
{
int ret;//产品内容
struct SPSCQueue*next;
int count;
}SPSCQueue;
SPSCQueue* head;//头节点
void*producer(void*arg)
{
SPSCQueue*temp;
while(1)
{
temp=(SPSCQueue*)malloc(sizeof(SPSCQueue));
temp->ret=rand()%1000+1;
head->count++;
printf("产生一个产品%d:%d\n",++i,temp->ret);
SPSCQueue*t;
pthread_mutex_lock(&lock);//加锁
if(head->count==MAX)//若果已到达最大容量,停止生产等待消费者消费
{
pthread_cond_wait(&cond,&lock);
}
t=head->next;
head->next=temp;
temp->next=t;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);
sleep(rand()%2);
}
return NULL;
}
void*consumer(void*arg)
{
SPSCQueue*temp;
while(1)
{
pthread_mutex_lock(&lock);//加锁
if(head->next==NULL)
{
pthread_cond_wait(&cond,&lock);//如果没有产品,等待生产者生产
}
temp=head;
while(temp->next->next!=NULL)//找到尾节点的上一个
{
temp=temp->next;
}
printf("消费一个产品%d:%d\n",i--,temp->next->ret);
head->count--;
free(temp->next);
temp->next=NULL;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);
sleep(rand()%5);
}
return NULL;
}
int main()
{
head=(SPSCQueue*)malloc(sizeof(SPSCQueue));
head->next=NULL;
head->count=0;
pthread_t pid,cid;
int ret;
ret=pthread_create(&pid,NULL,producer,NULL);
err_thread(ret,"create error");
ret=pthread_create(&cid,NULL,consumer,NULL);
err_thread(ret,"create error");
ret=pthread_join(pid,NULL);
err_thread(ret,"join error");
ret=pthread_join(cid,NULL);
err_thread(ret,"join error");
ret=pthread_mutex_destroy(&lock);
err_thread(ret,"mutex destroy error");
ret=pthread_cond_destroy(&cond);
err_thread(ret,"cond destroy error");
return 0;
}
方法二(信号量)
思路概述
创建两个信号量,product_num和blank_num,
product_num表示现在所拥有的产品的数量,初始化为0.
blank_num表示现在剩余容量,初始化为8.
生产者生产产品时,product_num++,blank_num- -;如果blank_num已经等于零则该线程进入阻塞等待,等待被唤醒。
消费者消费产品时,product_num- -,blank_num++;如果product_num已经等于零则该线程进入阻塞等待,等待被唤醒。
代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<semaphore.h>
int i=0;//产品序号
sem_t product_num,blank_num;
#define MAX 8
void err_thread(int ret,const char*str)//检查错误
{
if(ret!=0)
{
fprintf(stderr,"%s:%s\n",str,strerror(ret));
exit(1);
}
}
typedef struct SPSCQueue
{
int ret;//产品内容
struct SPSCQueue*next;
int count;
}SPSCQueue;
SPSCQueue* head;//头节点
void*producer(void*arg)
{
SPSCQueue*temp;
while(1)
{
sem_wait(&blank_num);
temp=(SPSCQueue*)malloc(sizeof(SPSCQueue));
temp->ret=rand()%1000+1;
head->count++;
printf("产生一个产品%d:%d\n",++i,temp->ret);
SPSCQueue*t;
t=head->next;
head->next=temp;
temp->next=t;
sem_post(&product_num);
sleep(rand()%2);
}
return NULL;
}
void*consumer(void*arg)
{
SPSCQueue*temp;
while(1)
{
sem_wait(&product_num);
temp=head;
while(temp->next->next!=NULL)//找到尾节点的上一个
{
temp=temp->next;
}
printf("消费一个产品%d:%d\n",i--,temp->next->ret);
head->count--;
free(temp->next);
temp->next=NULL;
sem_post(&blank_num);
sleep(rand()%5);
}
return NULL;
}
int main()
{
sem_init(&product_num,0,0);
sem_init(&blank_num,0,8);
head=(SPSCQueue*)malloc(sizeof(SPSCQueue));
head->next=NULL;
head->count=0;
pthread_t pid,cid;
int ret;
ret=pthread_create(&pid,NULL,producer,NULL);
err_thread(ret,"create error");
ret=pthread_create(&cid,NULL,consumer,NULL);
err_thread(ret,"create error");
ret=pthread_join(pid,NULL);
err_thread(ret,"join error");
ret=pthread_join(cid,NULL);
return 0;
}
多生产者多消费者队列(这里以3个生产者3个消费者为例)
方法一
思路概述
与单生产者单消费者队列类似,但次数会出现多个生产者和多个消费者,所以在判断产品数量为零消费者无法和产品数量达到上线生产者无法生产,要用循环去多次判断。
while(head->count==MAX)//若果已到达最大容量,停止生产等待消费者消费
{
pthread_cond_wait(&cond,&lock);
}
while(head->next==NULL)
{
pthread_cond_wait(&cond,&lock);//如果没有产品,等待生产者生产
}
代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
int i=0;//产品序号
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//互斥锁
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//互斥量
#define MAX 8//最大容量
void err_thread(int ret,const char*str)//检查错误
{
if(ret!=0)
{
fprintf(stderr,"%s:%s\n",str,strerror(ret));
exit(1);
}
}
typedef struct MPMCQueue
{
int ret;//产品内容
struct MPMCQueue*next;
int count;
}MPMCQueue;
MPMCQueue* head;//头节点
void*producer(void*arg)
{
MPMCQueue*temp;
while(1)
{
temp=(MPMCQueue*)malloc(sizeof(MPMCQueue));
temp->ret=rand()%1000+1;
pthread_mutex_lock(&lock);//加锁
while(head->count==MAX)//若果已到达最大容量,停止生产等待消费者消费
{
pthread_cond_wait(&cond,&lock);
}
head->count++;
printf("产生一个产品%d:%d\n",++i,temp->ret);
MPMCQueue*t;
t=head->next;
head->next=temp;
temp->next=t;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);
sleep(rand()%2);
}
return NULL;
}
void*consumer(void*arg)
{
MPMCQueue*temp;
while(1)
{
pthread_mutex_lock(&lock);//加锁
while(head->next==NULL)
{
pthread_cond_wait(&cond,&lock);//如果没有产品,等待生产者生产
}
temp=head;
while(temp->next->next!=NULL)//找到尾节点的上一个
{
temp=temp->next;
}
printf("消费一个产品%d:%d\n",i--,temp->next->ret);
head->count--;
free(temp->next);
temp->next=NULL;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);
sleep(rand()%2);
}
return NULL;
}
int main()
{
head=(MPMCQueue*)malloc(sizeof(MPMCQueue));
head->next=NULL;
head->count=0;
pthread_t pid1,pid2,pid3,cid1,cid2,cid3;
int ret;
ret=pthread_create(&pid1,NULL,producer,NULL);//生产者1
err_thread(ret,"create error");
ret=pthread_create(&pid2,NULL,producer,NULL);//生产者2
err_thread(ret,"create error");
ret=pthread_create(&pid3,NULL,producer,NULL);//生产者3
err_thread(ret,"create error");
ret=pthread_create(&cid1,NULL,consumer,NULL);//消费者1
err_thread(ret,"create error");
ret=pthread_create(&cid2,NULL,consumer,NULL);//消费者2
err_thread(ret,"create error");
ret=pthread_create(&cid3,NULL,consumer,NULL);//消费者3
err_thread(ret,"create error");
ret=pthread_join(pid1,NULL);
err_thread(ret,"join error");
ret=pthread_join(pid2,NULL);
err_thread(ret,"join error");
ret=pthread_join(pid3,NULL);
err_thread(ret,"join error");
ret=pthread_join(cid1,NULL);
err_thread(ret,"join error");
ret=pthread_join(cid2,NULL);
err_thread(ret,"join error");
ret=pthread_join(cid3,NULL);
err_thread(ret,"join error");
ret=pthread_mutex_destroy(&lock);
err_thread(ret,"mutex destroy error");
ret=pthread_cond_destroy(&cond);
err_thread(ret,"cond destroy error");
return 0;
}
方法二(信号量)
思路概述
和单生产者单消费者的思路类似,就不再过多赘述
代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<semaphore.h>
int i=0;//产品序号
sem_t product_num;
sem_t blank_num;
#define MAX 8//最大容量
void err_thread(int ret,const char*str)//检查错误
{
if(ret!=0)
{
fprintf(stderr,"%s:%s\n",str,strerror(ret));
exit(1);
}
}
typedef struct MPMCQueue
{
int ret;//产品内容
struct MPMCQueue*next;
int count;
}MPMCQueue;
MPMCQueue* head;//头节点
void*producer(void*arg)
{
MPMCQueue*temp;
while(1)
{
temp=(MPMCQueue*)malloc(sizeof(MPMCQueue));
temp->ret=rand()%1000+1;
sem_wait(&blank_num);
head->count++;
printf("产生一个产品%d:%d\n",++i,temp->ret);
MPMCQueue*t;
t=head->next;
head->next=temp;
temp->next=t;
sem_post(&product_num);
sleep(rand()%2);
}
return NULL;
}
void*consumer(void*arg)
{
MPMCQueue*temp;
while(1)
{
sem_wait(&product_num);
temp=head;
while(temp->next->next!=NULL)//找到尾节点的上一个
{
temp=temp->next;
}
printf("消费一个产品%d:%d\n",i--,temp->next->ret);
head->count--;
free(temp->next);
temp->next=NULL;
sem_post(&blank_num);
sleep(rand()%2);
}
return NULL;
}
int main()
{
sem_init(&product_num,0,0);//刚开始产品数量为0
sem_init(&blank_num,0,8);//刚开始空格为8
head=(MPMCQueue*)malloc(sizeof(MPMCQueue));
head->next=NULL;
head->count=0;
pthread_t pid1,pid2,pid3,cid1,cid2,cid3;
int ret;
ret=pthread_create(&pid1,NULL,producer,NULL);//生产者1
err_thread(ret,"create error");
ret=pthread_create(&pid2,NULL,producer,NULL);//生产者2
err_thread(ret,"create error");
ret=pthread_create(&pid3,NULL,producer,NULL);//生产者3
err_thread(ret,"create error");
ret=pthread_create(&cid1,NULL,consumer,NULL);//消费者1
err_thread(ret,"create error");
ret=pthread_create(&cid2,NULL,consumer,NULL);//消费者2
err_thread(ret,"create error");
ret=pthread_create(&cid3,NULL,consumer,NULL);//消费者3
err_thread(ret,"create error");
ret=pthread_join(pid1,NULL);
err_thread(ret,"join error");
ret=pthread_join(pid2,NULL);
err_thread(ret,"join error");
ret=pthread_join(pid3,NULL);
err_thread(ret,"join error");
ret=pthread_join(cid1,NULL);
err_thread(ret,"join error");
ret=pthread_join(cid2,NULL);
err_thread(ret,"join error");
ret=pthread_join(cid3,NULL);
err_thread(ret,"join error");
return 0;
}