• C++同步队列升级


    toc

    背景

    本来打算与升级后的线程池写一起发的,发现篇幅过长,可读性受到影响,故拆开发

    实现

    • 升级后的同步队列支持在队列内构造元素
      • 提高了性能,将原来的两次构造减少为一次构造(两次构造分别为,创建对象时构造一次,加入同步对队列时,拷贝/移动构造一次)
      • 有更高的灵活性,添加时不再限制于传入元素对象,可传入与元素不同类型的构造参数
      • 增加了使用限制,若想使用此功能,元素类必须实现相应构造参数的构造函数,否则编译错误
      • 调用者需保证构造函数所需参数与实际传递参数一致
    • 升级后的同步队列增加了判断元素是否存在,以及删除元素的功能
      • 队列本身就是一个先入先出的容器,自带了排序特性,所以可以使用二分查找
      • 增加了使用限制,需用户实现比较函数并传入

    代码

    #ifndef __SyncQueue_H_
    #define __SyncQueue_H_
    
    #include <list>
    #include <mutex>
    #include <utility>
    #include <condition_variable>
    
    template<typename T>
    class SyncQueue{
    public:
        SyncQueue() : m_iMaxCount(100), m_bStop(false){
        }
        SyncQueue(int iMaxCount) : m_iMaxCount(iMaxCount), m_bStop(false){
        }
        ~SyncQueue() = default;
    
        void Enqueue(const T& data){
            Append(data);
        }
    
        void Enqueue(T&& data){
            Append(std::forward<T>(data));    //转发data的原属性,此处转发data的右值引用
        }
    
        template<typename... Args>
        void Enqueue(Args&&... args){
            Append(std::forward<Args>(args)...);    
        }
    
        bool TryEnqueue(const T& data){
            return TryAppend(data);
        }
    
        bool TryEnqueue(T&& data){
            return TryAppend(std::forward<T>(data));
        }
    
        template<typename... Args>
        bool TryEnqueue(Args&&... args){
            return TryAppend(std::forward<Args>(args)...);
        }
    
        void Dequeue(T& data){
            std::unique_lock<std::mutex> lk(m_mMutex);
            m_cvNotEmpty.wait(lk, [this](){return m_bStop || !IsEmpty(); });
            if(m_bStop){
                return;
            }
            bool bNeedNotify = IsFull();
            data = m_listData.front();
            m_listData.pop_front();
            lk.unlock();
            if(bNeedNotify){
                m_cvNotFull.notify_one();
            }
        }
    
        bool TryDequeue(T& data){
            std::unique_lock<std::mutex> lk(m_mMutex);
            if(m_bStop || IsEmpty()){
                return false;
            }
            bool bNeedNotify = IsFull();
            data = m_listData.front();
            m_listData.pop_front();
            lk.unlock();
            if(bNeedNotify){
                m_cvNotFull.notify_one();
            }
            return true;
        }
    
        template<typename Compare, typename... Targets>
        bool Delete(const Compare& compare, const Targets&... targets){
            std::lock_guard<std::mutex> lk(m_mMutex);
            auto it = Find(compare, targets...);
            if(m_listData.end() == it){
                return false;
            } 
            m_listData.erase(it);
            return true;
        }
    
        template<typename Compare, typename... Targets>
        bool IsExist(const Compare& compare, const Targets&... targets){
            std::lock_guard<std::mutex> lk(m_mMutex);
            auto it = Find(compare, targets...);
            if(m_listData.end() == it){
                return false;
            }
            return true;
        }
    
        void Stop(){
            {
                std::lock_guard<std::mutex> lk(m_mMutex);
                m_bStop = true;
            }
            m_cvNotEmpty.notify_all();
            m_cvNotFull.notify_all();
        }
    
        int Size(){
            std::lock_guard<std::mutex> lk(m_mMutex);
            return Count();
        }
    
    private:
        template<typename... Args>
        bool TryAppend(Args&&...args){                                //实现通用引用
            std::unique_lock<std::mutex> lk(m_mMutex);
            if(m_bStop || IsFull()){
                return false;
            }
            bool bNeedNotify = IsEmpty();
            m_listData.emplace_back(std::forward<Args>(args)...);    //再次转发
            lk.unlock();
            if(bNeedNotify){
                m_cvNotEmpty.notify_one();
            }
            return true;
        }
    
        template<typename... Args>
        void Append(Args&&... args){                                //实现通用引用
            std::unique_lock<std::mutex> lk(m_mMutex);
            m_cvNotFull.wait(lk, [this](){return m_bStop || !IsFull(); });
            if(m_bStop){
                return;
            }
            bool bNeedNotify = IsEmpty();
            m_listData.emplace_back(std::forward<Args>(args)...);    //再次转发
            lk.unlock();
            if(bNeedNotify){
                m_cvNotEmpty.notify_one();
            }
        }
    
        bool IsFull(){
            return static_cast<int>(m_listData.size()) == m_iMaxCount;
        }
    
        bool IsEmpty(){
            return 0 == static_cast<int>(m_listData.size());
        }
    
        int Count(){
            return static_cast<int>(m_listData.size());
        }
        //二分查找
        template<typename Compare, typename... Targets>
        typename std::list<T>::iterator Find(const Compare& compare, const Targets&... targets){
            int iCount = Count();
            auto itLeft = m_listData.begin();
            while(iCount > 0){
                int iMiddleCount = iCount >> 1;
                auto itMiddle = std::next(itLeft, iMiddleCount);
                //目标大于middle,从middle右边二分,否则从左边二分
                if(compare(&(*itMiddle), targets...)){    
                    itLeft = std::next(itMiddle);
                    iCount -= iMiddleCount + 1;
                } else{
                    iCount = iMiddleCount;
                }
            }
            //经过前面的逻辑,itLeft会指向首个不小于targets对应对象
            //itLeft不大于,则找到,可返回,否则返回尾后迭代器
            if(m_listData.end() != itLeft && !compare(&(*itLeft), targets...)){
                return itLeft;
            } else{
                return m_listData.end();
            }
        }
    
        SyncQueue(const SyncQueue& rhs) = delete;
        SyncQueue(SyncQueue&& rhs) = delete;
        SyncQueue& operator=(const SyncQueue& rhs) = delete;
        SyncQueue& operator=(SyncQueue&& rhs) = delete;
    
    private:
        int m_iMaxCount;
        bool m_bStop;
        std::mutex m_mMutex;
        std::list<T> m_listData;
        std::condition_variable m_cvNotEmpty;
        std::condition_variable m_cvNotFull;
    };
    
    #endif //!__SyncQueue_H_
    
    • 利用std::list<>::emplace_back可在容器中构造对象的特性,搭配可变模板参数函数+ std::forward<>,来实现仅存入时构造一次的目的
    • 使用Enqueue、TryEnqueue的可变模板参数函数版本时,建议加上std::forward<>(前篇文章已经说明原因)
    • 在访问权限方法Find内,可调用对象compare的参数为,队列所存元素地址,因此传入Find的compare需为绑定所存元素成员函数指针的包装,或首个参数为所存元素地址的函数
    • Delete与IsExist依赖于Find,二者用同样的要求
    • 注意Find返回类型前的typename
    返回值std::list<T>::iterator中,iterator是个嵌套从属名称(其依赖于std::list<T>,并且T为模板参数),
    因此需要typename告诉编译器它是个类型,否则将编译不过
    在实例化std::list<T>以前,没法确定std::list<T>的具体类型,在编译Find时其具体类型还未确定,
    也就无法知道其内部iterator的是什么东西,所以编译不能通过




    原创不易,转载请注明出处,谢谢
  • 相关阅读:
    linux命令学习笔记:cut详解
    浏览器url传参中文时得到null的解决方法
    jQuery给控件赋值....
    Myeclipse 错误An internal error has occurred 解决办法
    浏览器发送URL的编码特性
    DEBUG -- CLOSE BY CLIENT STACK TRACE问题的两种解决方案,整理自网络
    解决Maven中OutOfMemory错误
    java中的URLEncoder和URLDecoder类;中文在地址栏中的处理
    关于CLOSE BY CLIENT STACK TRACE
    Hibernate复合主键映射
  • 原文地址:https://www.cnblogs.com/Keeping-Fit/p/15145353.html
Copyright © 2020-2023  润新知