写这篇博客的目的是因为笔者在学习c++11多线程的时候,查阅了许多博客和文章,发现他们都写的乱七八糟的,加上笔者想要通过类来封装消费者生产者问题,但是网上其他博客大多都没有封装成一个类,所以笔者才写下这篇博客,一来是与有同样疑惑与问题的读者共同学习,二来是记录下自己的学习经历,废话不多说,直接开干。
在正式开始之前,我们先来了解一下需要用到的函数的基本概念。
互斥量是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。举个例子,假如办公室里有且仅有一台打印机,当我们想要使用的时候,打印机这个时候就是一种临界资源,我们在使用他的时候声明,这台打印机在被我使用,那么其他人就不能够使用,因为此时这个临界资源被我占用了,而这种声明,就是一种互斥锁,一旦加了互斥锁,其他人就不能用了。
std::mutex mtx; //实例化一把锁
首先我们需要明确,什么是条件变量,或者说,什么是多线程下的条件变量。
在这里我们可以这样通俗的理解,条件变量就是只有当xxx条件成立的时候,才xxx,在多线程下就是只有某一进程下的xxx条件成立的时候,其他进程下才会xxx(一般是解除死循环)。
那么在这里,有两个函数可以实现条件变量:wait() 和notify()
std::condition_variable cond;
std::mutex mtx;
std::unique_lock lock(mtx);
//等待别的线程xxx成立
cond.wait(lock); //先上锁再解锁
//此线程xxx条件成立时,通知其他线程
cond.notify_one(); //通知一个线程
cond.notify_all(); //通知其他所有线程
这里不像std::pthread,我们可以直接调用std::thread(void)去创建一个线程,其接受的只需要一个void函数就行,比如:
void test1(){xxx}
int main(){
std::thread th(test1);
th.join();
}
但是本文所探讨的基础是建立在类上的,那么怎么使用类来构建多线程呢?其实也很简单,我们只需要多传入一个类对象就好了,比如这样:
class test{
public:
test(){};
void do(){xxx};
};
int main(){
std::shared_ptr sp(new test);
std::thread th(&test::do, test);
}
这样就可以了。
至此,基本上基本的函数都已介绍完毕,现在开始介绍代码思路。
其实代码思路可以分为以下几个部分:
- 生产item
- 消费item
- 生产者
- 消费者
我们一个一个部分的分析
在生产阶段,我们的目标只有一个,生产产品,但是这个行为是有前提的,就是当我们的篮子不满的时候,假设我们的篮子容量为10的话,我们当且篮子不等于10的时候才生产。
同样的道理,我们在消费阶段的目标也只有一个,消费产品,这个行为的前提是,当且篮子里还有item,也就是不等于0的时候才消费。
于是为了标记我们的生产与消费的进度,我么声明一个int[]队列,其中有两个坐标,分别是:
- int m_ConsPos;
- int m_ProdPos;
用来标记消费和生产的进度。
为了模拟真实的消费场景,我们需要让这个队列变成循环队列,也就是假设我们已经生产了10个,但是消费者已经消费掉前3个的时候,我们可以回到队首继续生产,也就是 storage[0,1,2]这几个位置。这样的话才可以真正的循环起来。
既然队列是循环的,那么我们又是怎么判断这个队列是空还是是满的呢?其实也很简单
我们前面已经标记过两个变量m_ConsPos和m_ProdPos呢,那么对于消费者,如果消费者消费掉一个之后发现m_ConsPos等于m_ProdPos的时候,那就说明消费者已经消费掉最后一个库存了,这个时候就需要等待生产者去生产,同理生产者一样,如果生产者生产一个之后发现自己m_ConsPos等于m_ProdPos的时候,就已经满了,那么落实到代码就是:
//对于生产者,这里因为有可能生产一个之后超过了队伍的长度,所以取余
while((this->m_ProdPos + 1) % basket_size == this->m_ConsPos){
std::cout << "Storage is full" << std::endl;
std::cout << "Waiting for consume" << std::endl;
this->m_NotFull.wait(lock);
}
//对于消费者
while(this->m_ProdPos == this->m_ConsPos){
std::cout << "Storage is empty!" << std::endl;
std::cout << "Waiting for produce" << std::endl;
this->m_NotEmpty.wait(lock);
}
至此,全部的逻辑就已经差不多了,剩下的贴上代码
const int basket_size = 10; //全局变量,假设我们的篮子一共有10个容量
class threadTest
{
public:
threadTest():m_ConsPos(0),m_ProdPos(0){}; //初始化消费和生产的进度为0
void produceItems(){ //生产函数
std::unique_lock lock(this->m_Mutex); //加锁
while((this->m_ProdPos + 1) % basket_size == this->m_ConsPos){ //为了避免溢出
std::cout << "Storage is full" << std::endl;
std::cout << "Waiting for consume" << std::endl; //这里篮子满了,暂时不需要生产了,等待消费
this->m_NotFull.wait(lock);
}
this->m_ProdPos++; //生产了一个产品
std::cout << "Produce item in pos : "<< this->m_ProdPos << std::endl;
if(this->m_ProdPos == basket_size){ //防止溢出
this->m_ProdPos = 0;
}
this->m_NotEmpty.notify_one(); //通知其他空的线程,如果有多个线程,需要使用notidy_all()
lock.unlock(); //解锁
}
void consumeItems(){ //消费者
std::unique_lock lock(this->m_Mutex); //加锁
while(this->m_ProdPos == this->m_ConsPos){ //如果没有库存,就等待生产
std::cout << "Storage is empty!" << std::endl;
std::cout << "Waiting for produce" << std::endl;
this->m_NotEmpty.wait(lock);
}
this->m_ConsPos++; //消费一个
std::cout << "Consume item in pos : " << this->m_ConsPos << std::endl;
if(this->m_ConsPos == basket_size){
this->m_ConsPos = 0;
}
this->m_NotFull.notify_one();
lock.unlock();
}
void producter(){
for(int i = 0; i < this->m_ItemTotal; i++){
sleep(1);
this->produceItems();
}
}
void consumer(){
for(int i = 0; i < this->m_ItemTotal; i++){
sleep(2);
this->consumeItems();
}
}
private:
std::mutex m_Mutex; //互斥锁
std::condition_variable m_NotFull; //如果篮子满的信号量
std::condition_variable m_NotEmpty; //如果篮子空的信号量
int m_ConsPos; //生产者进度
int m_ProdPos; //消费者进度
int m_ItemTotal = 100; //控制总进度
};
int main()
{
std::shared_ptr test(new threadTest);
threadTest test2;
std::thread th1(&threadTest::producter,test);
std::thread th2(&threadTest::consumer,test);
th1.join();
th2.join();
}
chen@Bionic:~/Code$ cd "/home/kuang/Code/" && g++ draft.cpp -o draft -lpthread && "/home/kuang/Code/"draft
Produce item in pos :1
Produce item in pos :2
Consume item in pos :1
Produce item in pos :3
Consume item in pos :2
Produce item in pos :4
Produce item in pos :5
Consume item in pos :3
Produce item in pos :6
Produce item in pos :7
Consume item in pos :4
Produce item in pos :8
Produce item in pos :9
Consume item in pos :5
Produce item in pos :10 //已经满了
Produce item in pos :1
Consume item in pos :6
Produce item in pos :2
Produce item in pos :3
Consume item in pos :7
Produce item in pos :4
Produce item in pos :5
Consume item in pos :8
Produce item in pos :6
Produce item in pos :7
Consume item in pos :9
Produce item in pos :8
Storage is full
Consume item in pos :10
Produce item in pos :9
Storage is full
Consume item in pos :1
Produce item in pos :10
Storage is full
Consume item in pos :2
Produce item in pos :1
Storage is full