线程安全的 stack

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

线程安全的 stack的相关文章

Stack的三种含义【转】

学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈". 理解这个概念,对于理解程序的运行至关重要.容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分. 含义一:数据结构 stack的第一种含义是一组数据的存放方式,特点为LIFO,即后进先出(Last in, first out). 在这种数据结构中,数据像积木那样一层层堆起来,后面加入的数据就放在最上层.使用的时候,最上层的数据第一个被用掉,这就叫做"后进先出". 与这种结构配套的,是

C#创建安全的栈(Stack)存储结构

在C#中,用于存储的结构较多,如:DataTable,DataSet,List,Dictionary,Stack等结构,各种结构采用的存储的方式存在差异,效率也必然各有优缺点.现在介绍一种后进先出的数据结构. 谈到存储结构,我们在项目中使用的较多.对于Task存储结构,栈与队列是类似的结构,在使用的时候采用不同的方法.C#中栈(Stack)是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义:堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小.

从cpu负载到jstack分析线程状态

示例代码: public class CPULockTest { private static Object lock1 = new Object(); private static Object lock2 = new Object(); public static void main(String[] args) { new Thread(()->{ synchronized (lock1){ try { System.out.println(Thread.currentThread().g

VC++深入详解——第15章:多线程,进程,线程

1. 进程: (1)程序与进程 程序是计算机指令的集合,文件形式存储在计算机磁盘上. 进程是程序执行的一个实例,是一个程序在其地址空间的一次执行活动. 一个程序可以对应着多个进程. 进程是资源申请,调度看,独立运行的单位. (2)进程的组成: 内核对象:系统用来存放进程的相关统计信息的地方,是操作系统内部分配的一个内存块. 地址空间:包含所有可执行模块或DLL模块的代码和数据,还包含动态分配的内存空间,线程的栈和堆分配的空间. (3)进程不执行任何程序,进程只是线程的容器,是线程执行程序的环境.

Stack的三种含义(数据超过栈的大小,就发生stack overflow)

非常典型的基础知识,转自http://www.ruanyifeng.com/blog/2013/11/stack.html 学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈". 理解这个概念,对于理解程序的运行至关重要.容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分. 含义一:数据结构 stack的第一种含义是一组数据的存放方式,特点为LIFO,即后进先出(Last in, first out). 在这种数据结构中,数据像积木那样一层层堆起来,后面加

Java数据结构漫谈-Stack

Stack(栈)是一种比较典型的数据结构,其元素满足后进先出(LIFO)的特点. Java中Stack的实现继承自Vector,所以其天然的具有了一些Vector的特点,所以栈也是线程安全的. class Stack<E> extends Vector<E> { 事实上,除了继承自Vector的那些方法之外,Stack只提供了5个方法: public E push(E item) { addElement(item); return item; } public synchroni

Stack的三种含义(转载--阮一峰)

作者: 阮一峰 学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈". 理解这个概念,对于理解程序的运行至关重要.容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分. 含义一:数据结构 stack的第一种含义是一组数据的存放方式,特点为LIFO,即后进先出(Last in, first out). 在这种数据结构中,数据像积木那样一层层堆起来,后面加入的数据就放在最上层.使用的时候,最上层的数据第一个被用掉,这就叫做"后进先出". 与这

Stack的三种含义

学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈". 理解这个概念,对于理解程序的运行至关重要.容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分. 含义一:数据结构 stack的第一种含义是一组数据的存放方式,特点为LIFO,即后进先出(Last in, first out). 在这种数据结构中,数据像积木那样一层层堆起来,后面加入的数据就放在最上层.使用的时候,最上层的数据第一个被用掉,这就叫做"后进先出". 与这种结构配套的,是

关于协程的学习 &amp; 线程栈默认10M

先看的这篇文章:http://blog.csdn.net/qq910894904/article/details/41699541 以nginx为代表的事件驱动的异步server正在横扫天下,那么事件驱动模型会是server端模型的终点吗? 我们可以深入了解下,事件驱动编程的模型. 事件驱动编程的架构是预先设计一个事件循环,这个事件循环程序不断地检查目前要处理的信息,根据要处理的信息运行一个触发函数.其中这个外部信息可能来自一个目录夹中的文件,可能来自键盘或鼠标的动作,或者是一个时间事件.这个触