整理自c++并发编程实战
/*
* threadsafe_stack.cpp
*
* Created on: Nov 30, 2017
* Author: clh01s@163.com
* 线程安全的栈
*/
#include <exception>
#include <iostream>
#include <mutex>
#include <stack>
#include <memory>
#include <string>
using namespace std;
struct empty_stack: exception
{
const char* what() const throw();
};
template<typename T>
class threadsafe_stack
{
public:
threadsafe_stack(){}
threadsafe_stack(const threadsafe_stack& other)
{
lock_guard<mutex> lock(other.m);
data = other.data;
}
//禁止使用=
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
void push(T new_value)
{
//调用锁
lock_guard<mutex> lock(m);
/* 调用data.push()可能会抛出异常
* 如果复制或者移动数据项抛出异常或
* 者不能分配足够的内存来扩展下层的
* 数据结构。
* 不管怎样,stack保证了它是安全的
* 因此这不是一个问题。
*/
cout<<"push value = "<<new_value<<endl;
data.push(move(new_value));
}
shared_ptr<T> pop()
{
lock_guard<mutex> lock(m);
/* 在函数pop()重载的第一种形式中
* 他的代码可能会抛出一个empty_stack
* 异常,但是没有做任何修改,因此它时安全的
*/
if(data.empty()) throw empty_stack();
/* 创建res可能会抛出异常
* 1.调用make_shared可能会抛出异常
* 因为它无法为新对象和需要引用计数的
* 内部数据分配内存。
* 2.复制构造函数或移动构造函数中返回
* 数据项被复制/移动到新分配的内存时也
* 可能会抛出异常。
* 这两种情况下c++运行库和标准库确保没
* 有内存泄露并且新对象被正确销毁。因为
* 你仍然没有修改下层的栈所以没有问题。
*/
shared_ptr<T> const res(
make_shared<T>(move(data.top())));
//调用data.pop()保证了不会抛出异常,因此pop()的重载是异常安全的
data.pop();
return res;
}
void pop(T& value)
{
lock_guard<mutex> lock(m);
if(data.empty()) throw empty_stack();
/* 这次于第一次时类似的,只不过这次是拷贝赋值
* 或移动赋值操作符抛出异常,而不是构造新对象
* 和一个shared_ptr实例抛出异常
*/
value = move(data.pop());
//这于上一个重载相同不会抛出异常
data.pop();
}
bool empty() const
{
lock_guard<mutex> lock(m);
return data.empty();
}
private:
stack<T> data;
mutable mutex m;
};
int main()
{
threadsafe_stack<string> a;
a.push("aa");
a.push("bb");
return 0;
}
程序输出:
clh@clh:~/testcode/并发$ g++ threadsafe_stack.cpp -std=c++11
clh@clh:~/testcode/并发$ ./a.out
push value = aa
push value = bb
转载请注明源地址:http://blog.youkuaiyun.com/clh01s