1. 生产者-消费者问题 概述
生产-消费者模型
:一个或多个 生产者线程 产生数据并将其放入共享缓冲区,同时一个或多个 消费者线程 从该缓冲区中读取数据进行操作的情景。
缓冲区
是一个用于存储生产者产生数据的中间容器;缓冲区
的容量通常是有限的,只能容纳一定数量的数据项。
生产 - 消费者模型的核心问题: 1. 线程与线程之间的 同步
与 互斥
关系;生产者与消费者之间的 等待
和 唤醒
机制。
2. 阻塞队列
2.1 工作原理
-
队列为空时,消费者从队列中消费数据时,会被阻塞挂起,直至队列中有元素可用;
-
队列为满时,生产者向队列中生产数据时,会被阻塞挂起,直至队列中有空闲位置;
-
使用 条件变量(pthread_cond_t 类型)+ 锁(pthread_mutex_t 类型)实现 等待 和 唤醒 机制。
2.2 框架
#include <iostream>
#include <queue>
#include <pthread.h>
template<class T>
class BlockQueue
{
public:
BlockQueue(int cap) :_cap(cap), _producer_wait_num(0), _consumer_wait_num(0)
{
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_producer_cond, nullptr);
pthread_cond_init(&_consumer_cond, nullptr);
}
~BlockQueue()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_producer_cond);
pthread_cond_destroy(&_consumer_cond);
}
private:
queue<T> _block_queue; // 生产者生产任务,消费者处理任务
int _cap; // 容量
pthread_mutex_t _mutex; // 保护共享资源的互斥锁
pthread_cond_t _producer_cond; // 用于唤醒生产者生产
pthread_cond_t _consumer_cond; // 用于唤醒消费者消费
int _producer_wait_num; // 记录阻塞挂起的生产者数量
int _consumer_wait_num; // 记录阻塞挂起的消费者数量
};
pthread_mutex_t _mutex; // 保护共享资源的互斥锁
解释 “为什么消费者和生产者共用一把锁” :
-
阻塞队列是一个共享资源,生产者和消费者都需要访问同一段内存资源来插入和删除元素;为了防止数据竞争和不一致,必须保证同一时间只有一个线程可以访问队列。
-
使用单个锁可以确保生产者和消费者访问队列时不会发生冲突。
2.3 生产
与 消费
- 生产
template<class T>
class BlockQueue
{
public:
void Enqueue(T& in)
{
phtread_mutex_lock(&_mutex); // 加锁
while (_block_queue.size() == _cap) // 阻塞队列为满
{
++_producer_wait_num;
pthread_cond_signal(&_consumer_cond); // 唤醒消费者线程
pthread_cond_wait(&_producer_cond, &_mutex); // 阻塞挂起, 释放锁
--_producer_wait_num;
}
_block_queue.push(in);
if (_consumer_wait_num > 0) // 唤醒 阻塞挂起的消费者线程
pthread_cond_signal(&_consumer_cond);
pthread_mutex_unlock