CP模型有三个部分:productor和consumer(由线程组成的功能块)还有一个特定结构的内存空间
以超市为例,超市货物由供应商提供,超市暂时存储货物,消费者可以在超市购买货物。
工作流程;生产者获取数据,生产任务并将任务放到仓库,消费者从仓库获取任务,处理任务
"321"原则
三种关系:
生产者和生产者之间的关系:互斥
消费者和消费者之间的关系:互斥
生产者和消费者之间的关系:互斥,同步(必须生产者生产之后,消费者才能消费,要有顺序)
**二种角色:**生产和消费
**一个交易所:**特定的内存结构空间
优点:
1.支持忙闲不均,如果仓库为满,生产者可以不生产一段时间,消费者可以一直消费
2.生产和消费解耦合,因为有仓库作缓冲
3.提高效率:在生产者获取数据和生产任务的时候,消费者可以处理数据,二者可以同时进行;生产者和消费者是多线程,多个线程同时完成获取数据和处理任务的工作。
代码模拟:
cpp
//block_queue.hpp
#include <queue>
#include <mutex>
#include <condition_variable>
#define MAX_CAPACITY 100
template <class T>
class blockqueue
{
public:
std::queue<T> q;
int max_capcity = MAX_CAPACITY; // 最大存储数量
std::mutex pmut; // 生产者锁
std::mutex cmut; // 消费者锁
std::condition_variable pcon;
std::condition_variable ccon;
blockqueue()
{
}
~blockqueue()
{
}
void push(T &task);
void pop();
bool empty();
size_t size();
T front();
};
template<class T>
void blockqueue<T>::push(T &task)
{
q.push(task);
}
template<class T>
void blockqueue<T>::pop()
{
q.pop();
}
template<class T>
bool blockqueue<T>::empty()
{
return q.empty();
}
template<class T>
size_t blockqueue<T>::size()
{
return q.size();
}
template<class T>
T blockqueue<T>::front()
{
return q.front();
}
cpp
//main.cpp
#include <iostream>
#include <ctime>
#include <thread>
#include <unistd.h>
#include "block_queue.hpp"
using namespace std;
void product(blockqueue<string> &bq)
{
unique_lock<mutex> ul(bq.pmut);
while (1)
{
//获取数据
int n = rand() % 10000;
cout << "获取数据:" << to_string(n) << endl;
sleep(1);
while (bq.size() >= bq.max_capcity)
{
// 不使用if,因为可能造成伪唤醒
cout << "任务已满,等待" << endl;
bq.pcon.wait(ul);
}
cout << "生成任务" << endl;
string name = "task" + to_string(n);
bq.push(name);
bq.ccon.notify_all();
sleep(1);
}
}
void consume(blockqueue<string> &bq)
{
unique_lock<mutex> ul(bq.cmut);
while (1)
{
while (bq.empty())
{
cout << "没有任务,等待" << endl;
bq.ccon.wait(ul);
}
//获取任务
string name = bq.front();
cout << "get " << name << endl;
bq.pop();
bq.pcon.notify_all();
//sleep(1);
//处理任务
cout << "处理数据中。。。" << endl;
//sleep(1);
}
}
int main()
{
srand(time(nullptr));
blockqueue<string> bq;
//生产者消费者都可以由多个线程组成
thread p[10];
for(auto& e:p)
{
e = thread(product, ref(bq));
}
thread c[5];
for(auto& e:c)
{
e = thread(consume, ref(bq));
}
sleep(1);
for(auto& e:p)
{
e.join();
}
for(auto& e:c)
{
e.join();
}
return 0;
}
blockqueue<string>对象bq需要在多个线程之间共享,并且需要通过引用传递。使用std::ref可以确保这一点