C++11多线程消费者生产者问题

写这篇博客的目的是因为笔者在学习c++11多线程的时候,查阅了许多博客和文章,发现他们都写的乱七八糟的,加上笔者想要通过类来封装消费者生产者问题,但是网上其他博客大多都没有封装成一个类,所以笔者才写下这篇博客,一来是与有同样疑惑与问题的读者共同学习,二来是记录下自己的学习经历,废话不多说,直接开干。

基本的语法与函数

在正式开始之前,我们先来了解一下需要用到的函数的基本概念。

互斥量std::mutex

互斥量是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。举个例子,假如办公室里有且仅有一台打印机,当我们想要使用的时候,打印机这个时候就是一种临界资源,我们在使用他的时候声明,这台打印机在被我使用,那么其他人就不能够使用,因为此时这个临界资源被我占用了,而这种声明,就是一种互斥锁,一旦加了互斥锁,其他人就不能用了。

std::mutex mtx; //实例化一把锁

条件变量std::condition_variable

首先我们需要明确,什么是条件变量,或者说,什么是多线程下的条件变量。

在这里我们可以这样通俗的理解,条件变量就是只有当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::thread

这里不像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

你可能感兴趣的:(c++,开发语言,多线程,并发编程)