Linux | 利用条件变量和互斥锁实现生产者和消费者模型

本文介绍了生产者消费者问题的线程模型,通过C++实现基于BlockingQueue和BlockingList的解决方案。生产者和消费者共享存储空间,当存储满或空时,相应线程会被阻塞。代码示例展示了如何使用互斥锁和条件变量来协调生产者和消费者的行为,确保数据的正确生产和消费。
摘要由CSDN通过智能技术生成

生产者和消费者问题是线程模型中的经典问题。所谓的生产者和消费者模型是指生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。


他们之间的关系:

  • 生产者与生产者之间是互斥关系,即同一时刻只能有一个生产者进行“生产”
  • 消费者与消费者之间是互斥关系,即同一时刻只能有一个消费者进行“消费”
  • 生产者与消费者之间是同步与互斥关系,即消费者“消费”和生产者“生产”不能同时进行

 基于BlockingQueue(阻塞队列)的生产者消费者模型

#include <iostream>
#include <queue>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

#define MAX_CAPACITY 5

using namespace std;
class Queue{
private:
  queue<int> _store; //初始化一个存放数据的队列
  int _capacity;//队列的总容量
  pthread_mutex_t mutexs;//互斥锁
  pthread_cond_t  cond_Preducer;//生产者的条件变量(既当满足这个条件时,生产者就可以进行相关的操作)
  pthread_cond_t  cond_Consumer;//消费者的条件变量(既当满足这个条件时,消费者就可以进行相关的操作)

public:
  Queue(int capacity = MAX_CAPACITY)//构造函数 将队列的总容量设为5
    :_capacity(capacity)
  {
    pthread_mutex_init(&mutexs, NULL);//初始化互斥锁
    pthread_cond_init(&cond_Consumer, NULL);//初始化消费者条件变量
    pthread_cond_init(&cond_Preducer, NULL);//初始化生产者条件变量
  }
  ~Queue()//析构函数
  {   
    pthread_mutex_destroy(&mutexs);//释放互斥锁所占用的资源
    pthread_cond_destroy(&cond_Consumer);//销毁消费者条件变量
    pthread_cond_destroy(&cond_Preducer);//销毁生产者条件变量
  }

  bool push(const int& data)//生产者将数据放入队列
  {
      pthread_mutex_lock(&mutexs);//上锁
      while(_capacity ==_store.size())//判断当前队列是否已满
      {//如果队列满,则
        pthread_cond_signal(&cond_Consumer);//至少唤醒一个阻塞的消费者线程,即标记消费者条件变量
        pthread_cond_wait(&cond_Preducer,&mutexs);//此时该生产者线程进入阻塞状态,并将互斥锁解锁
      }
      //如果队列不满,则
      _store.push(data);//插入数据
      pthread_cond_signal(&cond_Consumer);//至少唤醒一个阻塞的消费者线程,即标记消费者条件变量
      pthread_mutex_unlock(&mutexs);//解锁
    return true;
  }

  bool pop(int& data)//消费者将数据取出队列
  {
    pthread_mutex_lock(&mutexs);//上锁
    while(_store.empty())//判断队列是否为空
    {//如果队列为空,则
      pthread_cond_signal(&cond_Preducer);//至少唤醒一个阻塞的生产者者线程,即标记生产者条件变量
      pthread_cond_wait(&cond_Consumer,&mutexs);//此时该消费者线程进入阻塞状态,并将互斥锁解锁
    }
    //如果队列不为空,则
    data = _store.front();//从队列中取出一个元素,这里取出的是当前队列的第一个元素
    _store.pop();//pop操作
    pthread_cond_signal(&cond_Preducer);//至少唤醒一个阻塞的生产者线程,即标记生产者条件变量
    pthread_mutex_unlock(&mutexs);//解锁
    return true;
  }
};

void* producter(void* arg)//生产者线程函数
{
    srand((unsigned) time(NULL)); //随机种子函数
    Queue* p =  (Queue*)arg;
    while(1)
    {//生产者生产一个数据
        int data = rand()%11;
        p->push(data);
        printf("生产者生产了一个数据: %d\n",data);
    }
  return NULL;
}

void* consumer(void* arg)//消费者线程函数
{
  Queue* p = (Queue*)arg;
  int data = 0;
  while(1)
  {//消费者消费一个数据
    p->pop(data);
    printf("\t\t\t\t消费者使用了一个数据: %d\n", data);
  }
  return NULL;
}


int main()
{
  pthread_t Consumer;
  pthread_t Preducer;
  Queue arr; 

  pthread_create(&Consumer, NULL, consumer,(void*)&arr);//创建一个消费者线程

  pthread_create(&Preducer, NULL, producter,(void*)&arr);//创建一个生产者线程

  pthread_join(Consumer,NULL);//阻塞等待,并释放消费者线程所占用资源

  pthread_join(Preducer,NULL);//阻塞等待,并释放生产者线程所占用资源

  return 0;
}

使用以下命令进行编译链接

g++ -o main test.cpp -lpthread

【执行结果 】

基于BlockingList(阻塞链表)的生产者消费者模型 

#include<stdlib.h>
#include<assert.h>
#include<pthread.h>
#include<stdio.h>
#include<time.h>
#include<unistd.h>

typedef int elemtype;//元素类型
#define  MAX 20//链表的最大元素个数

typedef struct Node//节点类型
{
	elemtype data;
	struct Node *next;
}Node;

typedef struct list
{
    Node* head;//head永远指向链表的头节点
    Node* rear;//rear永远指向链表的尾节点
    int num = 0;//num用来记录链表中的元素个数
}list;

list List;//全局变量or共享资源 
pthread_cond_t cond_Preducer = PTHREAD_COND_INITIALIZER;//生产者的条件变量
pthread_cond_t cond_Consumer = PTHREAD_COND_INITIALIZER;//消费者的条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//互斥锁

void create_list(list* List)//初始化链表
{
	Node* head = (Node*)malloc(sizeof(Node));//头结点
	assert(head != NULL);

	head->next == NULL;
    List->head = head;
    List->rear = head;
    List->num = 0;
}

void Free_list(list* List)//释放链表
{
    Node* p = NULL;
    while(List->head != NULL)
    {
        p = List->head;
        List->head = List->head->next;
        free(p);
    }
    List->head = List->rear = NULL;//List是在栈上开辟的,进程结束时会自动释放
    List->num = 0;
}
    



void insert_list(list* List,elemtype data)//生产者将数据插入链表
{

	if(List->num >= MAX){//判断此时链表是否已满
		return;
	}
	
	Node* pnew = (Node*)malloc(sizeof(Node));
	assert(pnew != NULL);

	pnew->data = data;
    pnew->next = NULL;

	List->rear->next = pnew;
	List->rear = pnew;
	List->num++;
}


void delete_list(list* List)//消费者将数据从链表中取出
{
	if(List->num == 0){//判断链表是否为空
		return;
	}
    Node* p = List->head->next;
	List->head->next = List->head->next->next;
	free(p);
	List->num--;

	//删除最后一个节点时,要特殊处理
	if(List->num == 0){
		List->rear = List->head;
	}

}

//生产者线程函数
void *producer(void *arg)
{
	pthread_detach(pthread_self());//将该线程设为分离状态,及线程结束后会自动释放所占用资源
    srand((unsigned)time(NULL));//初始化随机种子
	while(1)
    {
        pthread_mutex_lock(&mutex);//上锁
		while(List.num == MAX)//如果此时链表已满
        {

            pthread_cond_signal(&cond_Consumer);//则至少唤醒一个阻塞线程 
			pthread_cond_wait(&cond_Preducer, &mutex);//等待自身条件
		}
        int data = rand()%11;
		insert_list(&List, data);
		printf("生产者生产了一个数据:%d\n",data);
		pthread_cond_signal(&cond_Consumer);//则至少唤醒一个阻塞线程 
		pthread_mutex_unlock(&mutex);//解锁
	}

}

void *consumer(void *arg)//消费者线程函数
{
	pthread_detach(pthread_self());
	while(1)
    {
		pthread_mutex_lock(&mutex);//上锁
		while(List.num == 0)
        {//没有产品,消费者就阻塞,直到被唤醒
			pthread_cond_signal(&cond_Preducer);则至少唤醒一个阻塞线程
			pthread_cond_wait(&cond_Consumer, &mutex);//等待自身条件
		}
		printf("\t\t\t\t消费者消费了一个数据:%d\n",List.head->next->data);
        delete_list(&List);
        pthread_cond_signal(&cond_Preducer);//则至少唤醒一个阻塞线程 
		pthread_mutex_unlock(&mutex);//解锁
	}
}

int main()
{
	create_list(&List);
	pthread_t Consumer;
    pthread_t Preducer;

	pthread_create(&Preducer, NULL, producer, NULL);

	pthread_create(&Consumer, NULL, consumer, NULL);

    
	getchar();//阻塞,不让主函数结束
    Free_list(&List);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值