基于Linux操作系统的生产消费者队列封装(C++)

一.先前代码及实现(在该篇中会用到)

1.基于Linux操作系统的锁的封装-CSDN博客

2.基于linux操作系统的线程封装(可实现任意传递任意类型任意个数的参数)-CSDN博客

二.生产消费者模型

        在一个多线程的进程中,通常存在如下关系生产者和消费者,其中生产者负责生产资源(产生任务)消费者负责消耗资源(获取任务),一般而言,消费者和消费者互斥,生产者和生产者互斥,消费者和生产者互斥且同步。我们将存放任务的地方(阻塞队列)称为交易场所。当阻塞队列满时,需要生产者停止发放任务,而为空时则需要消费者停止拿取任务。当阻塞队列由满变非满时,需要消费者唤醒生产者进行生产任务,反之亦然。

生产消费者模型
生产消费者模型

        由于同步和互斥的存在,以及额外的阻塞队列存在,生产者将任务递达至消费者 这个步骤会产生额外的开销,但是多消费者和多生产者可以充分利用CPU的多核,这使得生产者在生产任务,消费者在处理任务可以并行处理,极大的节约了生产任务和处理任务的总时长。

三.条件变量

        当阻塞队列(交易场所)由空变为非空时,可以利用条件变量唤醒消费线程,反之当阻塞队列由满变为非满时,也可以利用条件变量唤醒生产线程,条件变量可以极大的减少锁竞争所带来的开销。

有关条件变量的封装可参考基于Linux操作系统的锁的封装-CSDN博客

class condition_variable
    {
    public:
        condition_variable(const condition_variable&)=delete;
        condition_variable operator=(condition_variable)=delete;
        condition_variable(mutex& mutex):_mutex(mutex) //条件变量创建
        {
            if(pthread_cond_init(&_cond,nullptr))
            {
                std::cerr<<errno<<strerror(errno)<<std::endl;
                exit(1);
            }
        }
        ~condition_variable() //条件变量析构
        {
            if(pthread_cond_destroy(&_cond))
            {
                std::cerr<<errno<<strerror(errno)<<std::endl;
                exit(2);
            }
        }
        void wait() //当没有资源时则等待
        {
            if(pthread_cond_wait(&_cond,&_mutex.getmutex()))
            {
                std::cerr<<errno<<strerror(errno)<<std::endl;
                exit(3);
            }
        }
        void notify_one() //唤醒一个线程
        {
            if(pthread_cond_signal(&_cond))
            {
                std::cerr<<errno<<strerror(errno)<<std::endl;
                exit(4);
            }
        }
        void notify_all() //唤醒所有线程
        {
            if(pthread_cond_broadcast(&_cond))
            {
                std::cerr<<errno<<strerror(errno)<<std::endl;
                exit(5);
            }
        }
    private:
        mutex& _mutex;
        pthread_cond_t _cond;
    };

四.生产消费者队列封装(普通队列)

1.成员变量,构造和析构函数

    template<class T>
    class prodcon
    {
    public:
        typedef T task;
        prodcon(size_t max_task=normal_max_task)
        :_mutex(nullptr),_num_task(0),_max_task(max_task)
        ,_pro_cond(_mutex),_con_cond(_mutex)
        {
        }
        ~prodcon(){}
    private:
        size_t _num_task; //当前任务数量
        size_t _max_task; //最大任务数量
        std::queue<task> _qtask; //阻塞队列 task为放置任务
        condition_variable _pro_cond; //生产者条件变量,自定义封装
        condition_variable _con_cond; //消费者条件变量,自定义封装
        mutex _mutex; //互斥锁,自定义封装
    };

        所有的成员变量均有对应的析构函数,因此析构函数不需要做任何处理

2.放任务和出任务

        void push_task(task& in,int pri=10) //放入任务,pri是为了适配用优先级队列而引入的,无需注意
        {
            {
                lockguard<mutex> L(_mutex); //上锁
                while(isfull()) //判断是否满
                {
                    _pro_cond.wait(); //如果满则等待
                }
                _qtask.push(in); //放入任务
                ++_num_task; //当前任务+1
            }
            _con_cond.notify_one(); //唤醒线程,由于在唤醒线程时队列中一定存在任务,因此
//此处可以不加锁
        }
        void pop_task(task& out) //取得任务
        {
            {
                lockguard<mutex> L(_mutex); //加锁
                while(isempty()) //判断是不是为空
                {
                    _con_cond.wait(); 
                }
                out= std::move(_qtask.front());
                _qtask.pop(); //取任务
                --_num_task; //任务数量-1
            }
            _pro_cond.notify_one(); //唤醒生产者线程
        }
        int pop_some_tasks(task out[],int num)//取出多个任务
        {
            int real_num=0;
            {
                lockguard<mutex> L(_mutex);
                while(isempty())
                {
                    _con_cond.wait();
                }
                for(int i=0;i<num;++i)
                {
                    if(isempty()) break; //如果是空代表任务完成则返回
                    out[i]= std::move(_qtask.front());
                    ++real_num;
                    _qtask.pop();
                    --_num_task;
                }
            }
            _pro_cond.notify_all();//由于一次取出多个任务,因此可以
//全局唤醒所有线程
            return real_num; //返回取出了多少任务
        }
private:
        bool isfull() //判断是否为满
        {
            return !(_max_task-_num_task);
        }
        bool isempty() //判断是否为空
        {
            return !_num_task;
        }

3.其他成员函数

        

        bool full()
        {
            lockguard<mutex> L(_mutex); //上锁
            return !(_max_task-_num_task);
        }
        bool empty()
        {
            lockguard<mutex> L(_mutex); //上锁
            return !_num_task;
        }

        可以外界访问任务是否为空或者满,由于没有上锁因此要上锁,该函数不能成员函数中已上锁的时候调用,会发生死锁

五.生产消费者模型(优先级队列)

        优先级队列和普通队列执行一样,唯一的不同就是优先级队列在放入任务时需要设置该任务的优先级,这也是为什么在普通队列的放入任务中,会出现 int pri=10,这是为了适配优先级队列。另外需要注意,普通队列的插入和取出均为o(1),而优先级队列的插入和取出均为o(logn),如果非必要,不要使用优先级队列

1.完整代码(包括普通队列和优先级队列)

#ifndef _PRODCON_HPP
#define _PRODCON_HPP
#include<iostream>
#include<vector>
#include<memory>
#include<pthread.h>
#include<cstring>
#include<functional>
#include<unistd.h>
#include<queue>
#include"lock.hpp"

static const int normal_max_task=10;
static const int max_pri=100;
static const int min_pri=0;

namespace zwr
{
    template<class T>
    class prodcon
    {
    public:
        typedef T task;
        prodcon(size_t max_task=normal_max_task)
        :_mutex(nullptr),_num_task(0),_max_task(max_task)
        ,_pro_cond(_mutex),_con_cond(_mutex)
        {
        }
        ~prodcon(){}
        void push_task(task& in,int pri=10)
        {
            {
                lockguard<mutex> L(_mutex);
                while(isfull())
                {
                    _pro_cond.wait();
                }
                _qtask.push(in);
                ++_num_task;
            }
            _con_cond.notify_one();
        }
        void pop_task(task& out)
        {
            {
                lockguard<mutex> L(_mutex);
                while(isempty())
                {
                    _con_cond.wait();
                }
                out= std::move(_qtask.front());
                //std::cout<<"::"<<_qtask.front()<<std::endl;
                _qtask.pop();
                --_num_task;
            }
            _pro_cond.notify_one();
        }
        int pop_some_tasks(task out[],int num)
        {
            int real_num=0;
            {
                lockguard<mutex> L(_mutex);
                while(isempty())
                {
                    _con_cond.wait();
                }
                for(int i=0;i<num;++i)
                {
                    if(isempty()) break;
                    out[i]= std::move(_qtask.front());
                    ++real_num;
                    _qtask.pop();
                    --_num_task;
                }
            }
            _pro_cond.notify_all();
            return real_num;
        }
        bool full()
        {
            lockguard<mutex> L(_mutex);
            return !(_max_task-_num_task);
        }
        bool empty()
        {
            lockguard<mutex> L(_mutex);
            return !_num_task;
        }
    private:
        bool isfull()
        {
            return !(_max_task-_num_task);
        }
        bool isempty()
        {
            return !_num_task;
        }
        size_t _num_task;
        size_t _max_task;
        std::queue<task> _qtask;
        condition_variable _pro_cond;
        condition_variable _con_cond;
        mutex _mutex;
    };


    template<class T>
    class priority_prodcon //优先级队列
    {
    public:
        typedef T task;
        priority_prodcon(size_t max_task=normal_max_task)
        :_mutex(nullptr),_num_task(0),_max_task(max_task),_qtask(cmp())
        ,_pro_cond(_mutex),_con_cond(_mutex)
        {
        }
        ~priority_prodcon(){}
        void push_task(task& in,int pri=10) 
        {
            if(pri>max_pri) pri=max_pri;
            if(pri<min_pri) pri=min_pri;
            {lockguard<mutex> L(_mutex);
            while(isfull())
            {
               _pro_cond.wait();
            }
            _qtask.push(std::make_pair(in,pri));
            ++_num_task;}
            _con_cond.notify_one();
        }
        void pop_task(task& out)
        {
            {lockguard<mutex> L(_mutex);
            while(isempty())
            {
                _con_cond.wait();
            }
            out= std::move(_qtask.top().first);
            _qtask.pop();
            --_num_task;}
            _pro_cond.notify_one();
        }
        int pop_some_tasks(task out[],int num)
        {
            int real_num=0;
            {
                lockguard<mutex> L(_mutex);
                while(isempty())
                {
                    _con_cond.wait();
                }
                for(int i=0;i<num;++i)
                {
                    if(isempty()) break;
                    out[i]= std::move(_qtask.top().first);
                    ++real_num;
                    _qtask.pop();
                    --_num_task;
                }
            }
            _pro_cond.notify_all();
            return real_num;
        }
        bool full()
        {
            lockguard<mutex> L(_mutex);
            return !(_max_task-_num_task);
        }
        bool empty()
        {
            lockguard<mutex> L(_mutex);
            return !_num_task;
        }
    private:
     bool isfull()
        {
            return !(_max_task-_num_task);
        }
        bool isempty()
        {
            return !_num_task;
        }
        struct cmp
        {
            cmp(){}
            ~cmp(){}
            bool operator()(std::pair<task,int> a1,std::pair<task,int> a2)
            {
                if(a1.second>a2.second) return true;
                else return false;
            }
        };
        size_t _num_task;
        size_t _max_task;
        std::priority_queue<std::pair<task,int>,std::vector<std::pair<task,int>>,cmp> _qtask; //优先级队列
        condition_variable _pro_cond;
        condition_variable _con_cond;
        mutex _mutex;
    };
}

#endif

 

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值