线程安全的C ++堆栈

时间:2009-04-26 09:32:43

标签: c++ multithreading boost stack thread-safety

我是C ++的新手,我正在编写一个多线程应用程序,不同的编写器会将对象推入堆栈,读者将它们从堆栈中拉出来(或者至少将指针推到对象上)。

C ++中是否内置了可以在不添加锁定代码等的情况下处理此问题的结构?如果没有,那么Boost库呢?

编辑:

您好。感谢最初的好答案。我想我认为这可能是内置的一个原因是我纯粹在x86空间思考并且认为指针的PUSH / POP应该是指令级别的原子动作。

我不确定我最初的预感是否正确,但我想这并不一定适用于所有平台。虽然如果在x86上运行,你是否会将原子PUSH和POP放到堆栈中,如果是这样,这实际上是否使它无锁?

6 个答案:

答案 0 :(得分:21)

是的:Boost.Thread非常好,应该非常适合您的需求。 (现在,很多人都说你几乎可以将Boost视为内置功能。)

仍然没有可以开箱即用的类,但是一旦掌握了同步原语,实现自己的线程安全包装就非常简单,例如,{{ 1}}。它可能看起来像这样(没有实现每个方法......):

std::stack

如果您不熟悉C ++,请了解RAII。与此案相关的是,Boost.Thread拥有“范围锁定”类,因为忘记释放锁定而难以在腿部射击。

如果您发现自己编写了这样的代码:

template <typename T> class MyThreadSafeStack {
  public:
    void push(const T& item) {
      boost::mutex::scoped_lock lock(m_mutex);
      m_stack.push(item);
    }
    void pop() {
      boost::mutex::scoped_lock lock(m_mutex);
      m_stack.pop();
    }
    T top() const { // note that we shouldn't return a reference,
                    // because another thread might pop() this
                    // object in the meanwhile
      boost::mutex::scoped_lock lock(m_mutex);
      return m_stack.top();
    }

  private:
    mutable boost::mutex m_mutex;
    std::stack<T> m_stack;
}    

,那么你应该说不,转而使用RAII(语法不是直接来自Boost):

void doStuff() {
  myLock.lock();
  if (!condition) {
    reportError();
    myLock.unlock();
    return;
  }
  try {
    doStuffThatMayThrow();
  }
  catch (std::exception& e) {
    myLock.unlock();
    throw e;
  }
  doMoreStuff();
  myLock.unlock();
}

关键是当void doStuff() { scoped_lock lock; if (!condition) { reportError(); return; } doStuffThatMayThrow(); doMoreStuff(); } 对象超出范围时,它的析构函数会释放资源 - 在本例中为锁。无论您是通过抛出异常退出范围,还是执行您的同事在您的函数中间偷偷添加的奇怪的scoped_lock语句,或者只是到达函数的末尾,这都会发生。

答案 1 :(得分:4)

当前的C ++标准根本没有解决线程问题,所以第一个问题的答案是否定的。一般而言,将锁定构建到基本数据结构中是一个坏主意,因为它们没有足够的信息来正确和/或有效地执行它。相反,应该在使用数据结构的类中执行锁定 - 换句话说,在您自己的应用程序类中执行。

答案 2 :(得分:1)

AFAIK,没有内置的C ++支持。您必须使用简单的同步工具同步堆栈操作。如果线程属于同一个进程,则CriticalSection会执行,否则为Mutex。

答案 3 :(得分:1)

在C ++和Boost库中没有内置机制来支持这一点(注意:有些人写了thread-safe stacks/etc. in the Boost style)。您必须借用一些代码或在自己的同步中烹饪。

请注意,您的情况可能需要单写入器多读取器保护(SWMRG),其中多个写入器线程可以访问堆栈(但在给定时间点只有一个)并且多个读取器可以访问堆栈(许多在给定的时间点)。里希特有reference implementation

答案 4 :(得分:1)

如果您不想使用锁定,则需要使用无锁堆栈。这实际上并不那么难(无锁队列更难)。您确实需要特定于平台的比较交换原语,例如Windows上的InterlockedCompareExchange,但这并不难抽象。

请参阅此处以获取C#中的示例:

http://www.boyet.com/Articles/LockFreeRedux.html

答案 5 :(得分:0)

如果您在Windows上运行,SLIST会实现一个无锁堆栈(结构为SLIST_HEADER & SLIST_ENTRY)。

该算法使用互锁函数使用相当简单的推/弹单链表列表来实现。唯一不明显的项目是计数器增量以避免ABA问题。