C++线程安全的栈stack与队列queue类及其注意事项,摘自《C Concurrency In Action2》

1、线程安全队列类threadsafe_queue

#include <queue> 
#include <memory> 
#include <mutex> 
#include <condition_variable> 
template<typename T> 
class threadsafe_queue 
{
    private: 
    mutable std::mutex mut; // 1 互斥量必须是可变的 
    std::queue<T> data_queue; 
    std::condition_variable data_cond; 
    public: 
    threadsafe_queue() {}
    threadsafe_queue(threadsafe_queue const& other) 
    { 
        std::lock_guard<std::mutex> lk(other.mut); 
        data_queue=other.data_queue; 
    }
    void push(T new_value) 
    { 
        std::lock_guard<std::mutex> lk(mut); 
        data_queue.push(new_value); 
        data_cond.notify_one(); 
    }
    void wait_and_pop(T& value) 
    { 
        std::unique_lock<std::mutex> lk(mut); 
        data_cond.wait(lk,[this]{return !data_queue.empty();}); 
        value=data_queue.front(); 
        data_queue.pop(); 
    }
    std::shared_ptr<T> wait_and_pop() 
    { 
        std::unique_lock<std::mutex> lk(mut); 
        data_cond.wait(lk,[this]{return !data_queue.empty();}); 
        std::shared_ptr<T> res(std::make_shared<T> (data_queue.front())); 
        data_queue.pop(); 
        return res; 
    }
    bool try_pop(T& value) 
    { 
        std::lock_guard<std::mutex> lk(mut); 
        if(data_queue.empty()) return false; 
        value=data_queue.front(); 
        data_queue.pop(); 
        return true; 
    }
    std::shared_ptr<T> try_pop() 
    { 
        std::lock_guard<std::mutex> lk(mut); 
        if(data_queue.empty()) return std::shared_ptr<T>(); 
        std::shared_ptr<T> res(std::make_shared<T> (data_queue.front())); 
        data_queue.pop(); 
        return res; 
    }
    bool empty() const 
    { 
        std::lock_guard<std::mutex> lk(mut); 
        return data_queue.empty(); 
    } 
}; 

注意事项:

1、复制构造函数对形参threadsafe_queue const& other在拷贝前上锁。这是考虑到const&可以引用变量、常量或另一个const&,其中const&引用的变量可能并发地执行写操作,从而复制构造函数在拷贝other.data_queue前要锁定other.mut互斥量,防止other.data_queue拷贝到另外线程部分写的对象,这是读写同步。

2、最后的empty()实例方法声明为const的。考虑到实例变量、实例常量都可以调用const实例方法,所以就不必再定义非const的empty()实例方法了,实例变量、实例常量与const&统一调用const实例方法,只需要对const的empty()加读锁即可,避免读到部分写的共享数据,这是读写同步。比如另一个线程正在clear共享的threadsafe_queue队列实例,此时如果另一线程并发地调用empty()实例方法可能访问到部分clear的非空队列,随后进行读操作时,另一线程可能已经完成了clear()操作,从而读不到任何元素。读到部分写的对象也是如此。

3、因为锁住互斥量是个可变操作,所以互斥量字段必须修饰为mutable才能在empty()和拷贝构造函数中锁住。

2、线程安全的栈类threadsafe_stack

#include <exception> 
#include <memory> 
#include <mutex> 
#include <stack> 
struct empty_stack: std::exception 
{ 
    const char* what() const throw() { 
        return "empty stack!"; 
    }; 
};
template<typename T> 
class threadsafe_stack 
{
    private: 
    std::stack<T> data; 
    mutable std::mutex m; 
    public: 
    threadsafe_stack() : data(std::stack<T>()){} 
    threadsafe_stack(const threadsafe_stack& other) 
    { 
        std::lock_guard<std::mutex> lock(other.m); 
        data = other.data; // 1 在构造函数体中的执行拷贝 
    }
    threadsafe_stack& operator=(const threadsafe_stack&) = delete; 
    void push(T new_value) 
    { 
        std::lock_guard<std::mutex> lock(m); 
        data.push(new_value); 
    }
    std::shared_ptr<T> pop() 
    { 
        std::lock_guard<std::mutex> lock(m); 
        if(data.empty()) throw empty_stack(); // 在调用pop前,检查栈是否为空 
        std::shared_ptr<T> const res(std::make_shared<T> (data.top())); // 在修改堆栈前,分配出返回值 
        data.pop(); 
        return res; 
    }
    void pop(T& value) 
    { 
        std::lock_guard<std::mutex> lock(m); 
        if(data.empty()) throw empty_stack(); 
        value=data.top(); 
        data.pop(); 
    }
    bool empty() const 
    { 
        std::lock_guard<std::mutex> lock(m); 
        return data.empty(); 
    } 
}; 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值