线程安全队列(使用互斥锁进行实现)

线程安全队列(使用互斥锁进行实现)

没有设置队列上限的线程安全队列

只需要采取一个std::condition_variable变量,用于处理队列为空的情况

以下是示例代码,涉及了std::mutex和std::condition_variable、std::unique_lock、std::lockguard等多线程交互的类。
测试方式采取的是3个生成者和一个消费者,消费者监测生产者push的数据是否有异常,同时拿一个线程在十秒后调用Finish()函数。

示例1:没有设置队列上限的线程安全队列(包含测试代码)

#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <atomic>
#include <functional>
#include <string>
#include <chrono>
#include <map>

template <typename T>
class ThreadSafetyQueue
{
public:
    ThreadSafetyQueue() : finish_(false) {}


    bool push(T &data)
    {
        std::unique_lock<std::mutex> lock(mtx_);
        if(finish_)
        {
            std::cout << "queue finish" << std::endl;
            return false;
        }
        data_queue_.push(data);
        lock.unlock(); // 注意:解锁必须在通知之前
        cv_.notify_one();
        return true;
    }

    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(mtx_);
        while(!finish_ && data_queue_.empty()) // 防止假醒
        {
            cv_.wait(lock);
        }
        if(finish_)
        {
            std::cout << "queue finish" << std::endl;
            return false;
        }
        data = data_queue_.front();
        data_queue_.pop();
        return true;
    }

    bool empty()
    {
        std::unique_lock<std::mutex> lock(mtx_);
        if(data_queue_.empty())
        {
            return true;
        }
        return false;
    }

    int size()
    {
        std::unique_lock<std::mutex> lock(mtx_);
        return data_queue_.size();
    } 

    void Finish()
    {
        finish_ = true;
        cv_.notify_all();
    }

private:
    std::mutex mtx_;
    std::queue<T> data_queue_;
    std::condition_variable cv_;
    std::atomic_bool finish_;
};

struct test_data
{
    int num;
    std::string str;
};

int main()
{
    ThreadSafetyQueue<test_data> queue;
    std::function<void(std::string)> product_func = ([&queue](std::string name){
        for(int i = 0; i < 1000000; ++i)
        {
            test_data td{i, name};
            queue.push(td);
        }
    });

    std::function<void(void)> consumer_func = ([&queue](){
        std::map<std::string, int> test_mp;
        while(1)
        {
            test_data data;
            if(queue.pop(data))
            {
                if(test_mp.find(data.str) == test_mp.end())
                {
                    test_mp[data.str] = 0;
                    std::cout << "add consumer name is: " << data.str << std::endl;
                }

                if(test_mp[data.str] == data.num)
                {
                    test_mp[data.str] ++;
                }
                else
                {
                    std::cout << "error" << std::endl;
                    std::cout << "consumer name is: " << data.str << std::endl;
                    std::cout << "consumer " << data.num << std::endl;
                }
            }
        }
    });

    std::function<void(void)> finish_func = ([&queue](){
        std::this_thread::sleep_for(std::chrono::seconds(10));
        queue.Finish();
    });

    std::thread producer1(product_func, "producer1");
    std::thread producer2(product_func, "producer2");
    std::thread producer3(product_func, "producer3");
    std::thread consumer1(consumer_func);
    std::thread finish_thread(finish_func);

    while (1)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    
    queue.Finish();
    producer1.join();
    producer2.join();
    producer3.join();
    consumer1.join();
    finish_thread.join();
    
    return 0;
}

设置队列上限的线程安全队列

需要两个std::condition_variable变量,用于处理队列为空和队列为满的情况。
增加了设置MaxSize的函数。

示例中,size=0表示队列没有上限。

示例2:设置队列上限的线程安全队列(包含测试代码)
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <atomic>
#include <functional>
#include <string>
#include <chrono>
#include <map>

template <typename T>
class ThreadSafetyQueue
{
public:
    ThreadSafetyQueue(size_t size = 0) : 
    finish_{false},
    max_size_{size}
    {
    }


    bool push(T &data)
    {
        std::unique_lock<std::mutex> lock(mtx_);
        while (CheckFull() && !finish_)
        {
            // std::cout << "queue full" << std::endl;
            full_cv_.wait(lock);
        }
        
        if(finish_)
        {
            // std::cout << "queue finish" << std::endl;
            return false;
        }
        data_queue_.push(data);
        lock.unlock(); // 解锁在notify_one之前更合适
        empty_cv_.notify_one();
        return true;
    }

    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(mtx_);
        while(!finish_ && data_queue_.empty()) // 防止假醒
        {
            empty_cv_.wait(lock);
        }
        if(finish_)
        {
            // std::cout << "queue finish" << std::endl;
            return false;
        }
        data = data_queue_.front();
        data_queue_.pop();
        full_cv_.notify_one();
        return true;
    }

    bool empty()
    {
        std::unique_lock<std::mutex> lock(mtx_);
        if(data_queue_.empty())
        {
            return true;
        }
        return false;
    }

    bool full()
    {
        std::unique_lock<std::mutex> lock(mtx_);
        if(data_queue_.size() >= max_size_)
        {
            return true;
        }
        return false;
    }

    int size()
    {
        std::unique_lock<std::mutex> lock(mtx_);
        return data_queue_.size();
    } 

    void Finish()
    {
        finish_ = true;
        full_cv_.notify_all();
        empty_cv_.notify_all();
    }

    void SetMaxSize(size_t size)
    {
        std::unique_lock<std::mutex> lock(mtx_);
        max_size_ = size;
        lock.unlock();
        full_cv_.notify_all();
    }

private:
    std::mutex mtx_;
    std::queue<T> data_queue_;
    std::condition_variable full_cv_;
    std::condition_variable empty_cv_;
    volatile size_t max_size_;
    std::atomic_bool finish_;

    bool CheckFull()
    {
        if(max_size_ == 0)
            return false;
        else 
            return data_queue_.size() >= max_size_;
    }
};

struct test_data
{
    int num;
    std::string str;
};

int main()
{
    ThreadSafetyQueue<test_data> queue(1000);
    std::function<void(std::string)> product_func = ([&queue](std::string name){
        for(int i = 0; i < 1000000; ++i)
        {
            test_data td{i, name};
            queue.push(td);
        }
    });

    std::function<void(void)> consumer_func = ([&queue](){
        std::map<std::string, int> test_mp;
        while(1)
        {
            test_data data;
            if(queue.pop(data))
            {
                if(test_mp.find(data.str) == test_mp.end())
                {
                    test_mp[data.str] = 0;
                    std::cout << "add consumer name is: " << data.str << std::endl;
                }

                if(test_mp[data.str] == data.num)
                {
                    test_mp[data.str] ++;
                }
                else
                {
                    std::cout << "error" << std::endl;
                    std::cout << "consumer name is: " << data.str << std::endl;
                    std::cout << "consumer " << data.num << std::endl;
                }
            }
        }
    });

    std::function<void(void)> finish_func = ([&queue](){
        std::this_thread::sleep_for(std::chrono::seconds(10));
        queue.Finish();
    });

    std::thread producer1(product_func, "producer1");
    std::thread producer2(product_func, "producer2");
    std::thread producer3(product_func, "producer3");
    std::thread consumer1(consumer_func);
    std::thread finish_thread(finish_func);

    while (1)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    
    queue.Finish();
    producer1.join();
    producer2.join();
    producer3.join();
    consumer1.join();
    finish_thread.join();
    
    return 0;
}



posted @   227569hy  阅读(59)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示