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的是什么东西,所以编译不能通过




posted @ 2021-08-15 22:16  無雙  阅读(106)  评论(0编辑  收藏  举报