stack 不是一种容器, 而是一种适配器, 它的实现大概是这样的:
template<typename T, typename Container = deque<T> > class stack { public: explicit stack (const Container&); explicit stack (Container&& = Container ()); template<class Alloc> explicit stack (const Alloc&); template<class Alloc> stack ( const Container&, const Alloc&); template<class Alloc> stack (Container&&, const Alloc&); template<class Alloc> stack (stack&&, const Alloc&); bool empty () const; size_t size () const; T& top (); T const& top () const; void push (T const&); void push (T&&); void pop (); void swap (stack&&); };
可是这个版本并不是线程安全的, 比如:
void oops () { stack<int> s; if (!s.empty()) { int const value = s.top (); s.pop (); //do_something (value); } }
假设这么一种情况, 对于只有一个元素的 s,线程 A 和线程 B 当前都已执行了 if 中的判断, 此时线程 A 得到了 value 的值, 然后执行 pop()。 但是, 在这之后,执行了线程的切换, 此时线程 B 开始师徒试图给 value 赋值, 可早已人去楼空。
那么, 怎样才能在线程安全的前提下得到栈顶的值并在复制之后 pop 它呢? 答案是这样:
template<typename Stack> void empty_stack_check (const Stack& s) { if (s.empty ()) { throw empty_stack (); } } struct empty_stack : std::exception { const char* what () const throw(); }; template<typename T> class thread_safe_stack { private: stack<T> data; mutable mutex m; public: using lockGuard = lock_guard<mutex>; thread_safe_stack () {} thread_safe_stack (const thread_safe_stack& other) { lockGuard lock (other.m); data = other.data; } stack& operator=(const thread_safe_stack&) = delete; void push (T new_value) { lockGuard lock (m); data.push (new_value); } shared_ptr<T> pop () { lockGuard lock (m); empty_stack_check (data); shared_ptr<T> const res = make_shared<T> (data.top()); data.pop (); return res; } void pop (T& value) { lockGuard lock (m); empty_stack_check (data); value = data.top (); data.pop (); } bool empty () const { lockGuard lock (m); return data.empty (); } };
时间: 2024-10-08 10:13:44