生产者消费者模型 C++
生产者消费者模型通过一个容器/缓冲区(比如阻塞队列)解决生产者消费者的强耦合问题。生产者和消费者之间通过阻塞队列来进行通讯。生产者产生数据直接扔进缓冲区中,消费者每次取时上锁,避免冲突,取完解锁处理业务逻辑。具体情况,视于队列空和满的条件决定。
实现逻辑
方法:这里采用互斥锁+两个条件变量实现
当队列为空时,消费者线程阻塞,唤醒生成者线程生产;
当队列为满时,生产者线程阻塞,唤醒消费者线程消费。
- 具体的实现逻辑是构建一个queue来存储生产的数据,阻塞队列不满时可以生产,不空时可以消费。
- 首先实现构造函数,初始化一个unique_lock供condition_variable使用。
- 互斥锁构造列表初始化,然后函数体里unlock。
- 创建两个条件变量,m_cond_Full和m_cond_Empty;
- m_cond_Full ,超出缓冲区(队列阻塞),上锁m_cond_Full.wait(locker) ,唤醒消费者线程;
m_cond_Empty,缓冲区为空时,上锁m_cond_Empty.wait(locker),并唤醒生产者线程;
- 之后就是入和出队列的细节如下,具体看源码。
面试现场测试也没问题~
代码实现
- thread 消费队列写法
#include <thread>
#include <queue>
#include <iostream>
#include <condition_variable>
//#include<invoke.h>
#include <mutex>
template <typename T> //生产者消费者模式
class Queue
{
public:
Queue(int maxn = 20) : thread_maxnum(maxn) {}
//最大线程数量与CPU核数相关
//生产者
bool Push(const T &val)
{
std::unique_lock<std::mutex> locker(m_mtx);
if (mq.size() >= thread_maxnum)
{
m_cond_Full.wait(locker); //超出缓冲区加锁
return false;
}
m_cond_Empty.notify_all(); //解锁互斥量,唤醒消费者线程
mq.push(val);
return true;
}
//消费者
T Pop()
{
std::unique_lock<std::mutex> locker(m_mtx);
while (mq.empty())
{
m_cond_Empty.wait(locker); //缓冲队列为空上锁,阻塞消费者线程
}
T val = mq.front();
mq.pop();
m_cond_Full.notify_all(); //解锁互斥量,唤醒生产者线程
return val;
}
private:
std::queue<T> mq;
std::mutex m_mtx; //互斥锁
int thread_maxnum; //最大线程数量,缓冲区数量
std::condition_variable m_cond_Empty; //条件变量,对应消费者线程
std::condition_variable m_cond_Full; //条件变量,对应生产者线程
};
void producer(Queue<int> *q)
{
for (int i = 0; i < 1000; i++)
{
if (q->Push(i))
std::cout << "push: " << i << "\n";
else
std::cout << "push failed!\n";
//std::this_thread::sleep_for(std::chrono::seconds(1000));
}
}
void consumer(Queue<int> *q)
{
for (int i = 0; i < 500; i++)
{
auto t = q->Pop();
std::cout << "pop: " << t << "\n";
//std::this_thread::sleep_for(std::chrono::seconds(500));
}
}
int main()
{
Queue<int> q(20);
std::thread t1(producer, &q);
std::thread t2(consumer, &q);
t1.join();
t2.join();
return 0;
}
- pthread 线程写法
#include <pthread.h>
#include <iostream>
#include <error.h>
using namespace std;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static bool flag = true;
void *producer(void *)
{
for (int i = 0; i < 60; i++)
{
pthread_mutex_lock(&mutex);
while (!flag)
pthread_cond_wait(&cond, &mutex);
cout << "thread1:" << i * 2 << "\n";
flag = false;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
}
}
void *consumer(void *)
{
for (int i = 0; i < 60; i++)
{
pthread_mutex_lock(&mutex);
while (flag)
pthread_cond_wait(&cond, &mutex);
cout << "thread2:" << i * 2 + 1 << "\n";
flag = true;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
}
}
int main()
{
int iRet = -1;
pthread_t rask1, rask2;
iRet = pthread_create(&rask1, NULL, &producer, NULL);
if (iRet != 0)
{
cerr << "producer star error\n";
}
iRet = pthread_create(&rask2, NULL, &consumer, NULL);
if (iRet != 0)
{
cerr << "consumer star error\n";
}
pthread_join(rask1, NULL);
pthread_join(rask2, NULL);
return 0;
}