用BlockBoundQueue和c++11实现多线程生产者消费者问题

来源:互联网 发布:淘宝如何取消超级推广 编辑:程序博客网 时间:2024/05/18 10:00

最近在读到陈硕的《linux多线程服务端编程》这书时,发现了两个特别好用的模板类 : BlockQueue和BlockBoundQueue,用来实现多线程中的生产者消费者问题是特别方便的。但是其源码中用到boost库,所以在这里我稍微修改下,实现如下。

这里只写出BlockBoundQueue,读者可自行写出BlockQueue

// file : blockBoundQueue.h#ifndef YANG_BLOCKBOUNDQUEUE#define YANG_BLOCKBOUNDQUEUE#include <mutex>#include <condition_variable>#include <queue>#include <cstdio>namespace yang{#define _FULL 4 //template <typename _Tp>class BlockBoundQueue{public:    BlockBoundQueue(size_t bound = _FULL) :bound_(bound){}    BlockBoundQueue(const BlockBoundQueue&) = delete;    BlockBoundQueue& operator=(const BlockBoundQueue&) = delete;    void push(const _Tp& value)    {        std::unique_lock<std::mutex> lck(mutex_);// 利用RAII技法,将mutex_托管给lck        while (count_ + 1 == bound_)// 用while防止虚假唤醒         {            printf("the queue is full , waiting for the consumer consuming !\n");            notFull_.wait(lck); //等待队列非满条件发生        }        count_++;        queue_.push(value);        notEmpty_.notify_one();//通知队列非空,不能用all,读者自行思考为什么    }    _Tp get()     {        std::unique_lock<std::mutex> lck(mutex_);        while (queue_.empty())        {            printf("the queue is empty , waiting for the producer producing !\n");            notEmpty_.wait(lck);//等待队列为非空        }        _Tp front(queue_.front());        queue_.pop();        count_--;        notFull_.notify_one();//通知队列为非满,请注意不能用all        return front;    }private:    std::mutex mutex_;    std::condition_variable notEmpty_;    std::condition_variable notFull_;    std::queue<_Tp> queue_;    size_t count_{ 0 };    size_t bound_;};}#endif//在visual studio 2013上编译运行通过

分析上面代码,首先std::queue容器并没有提供full()这个成员函数,所以我们要自己维护一个size_t type 的变量count_,并初始化为0 。其次,我们要禁用掉拷贝构造和赋值函数,只保留构造函数即可。最重要的两个成员函数是push()get(),在这里每次进入这两个函数之后,先加锁,这里我们用到了RAII的技法来保证我们的加锁操作是异常安全的,并且通过两个条件变量notEmpty 和 notFull 的配合,实现了线程安全的出队和入队操作。

下面我们来实现下,篇幅的原因,直接上多生产者多消费者模式,因为其他的几种都是它的特列。

// file : main.cpp#include <cstdio>#include <cstdlib>#include <thread>#include "BlockBoundQueue.h"yang::BlockBoundQueue<int> blockboundqueue_;// 全局的缓冲边界队列const int total = 16; // 商品总数void consumer(size_t id,size_t n); // 消费者void producer(size_t id,size_t n); // 生产者int main(){    std::thread consumer1(consumer,0, 5);    std::thread consumer2(consumer,1, 5);    std::thread consumer3(consumer,2, 6);    std::thread producer1(producer,0, 8);    std::thread producer2(producer,1, 8);    consumer1.join();    consumer2.join();    consumer3.join();    producer1.join();    producer2.join();    system("pause");    return 0;}void consumer(size_t id,size_t n){    for (auto i = 0; i < n; ++i)    {        std::this_thread::sleep_for(std::chrono::seconds(2));        int item = blockboundqueue_.get();        printf("the %d^th consumer thread has consumed the %d^th item\n", id,item);    }}void producer(size_t id,size_t n){    for (int i = 0; i < n; ++i)    {        blockboundqueue_.push(i);        printf("the %d^th producer thread has produced the %d^th item\n",id, i);    }}

执行结果如下:
这里写图片描述

总结 : 利用BlockBoundQueue可以很方便的实现生产者消费者模式,并且其本身实现起来简洁优雅,我们应该学会使用。

参考资料:
Linux多线程服务端编程 陈硕著
http://www.cplusplus.com/reference/
https://baptiste-wicht.com/posts/2012/04/c11-concurrency-tutorial-advanced-locking-and-condition-variables.html
http://www.cnblogs.com/haippy/p/3236136.html

1 0