Потокобезопасный стек C++

Я новичок в C++ и пишу многопоточное приложение, в котором разные авторы будут толкать объекты в стек, а читатели вытаскивать их из стека (или, по крайней мере, нажимать указатель на объект)..

есть ли какие-либо структуры, встроенные в C++, которые могут обрабатывать это без добавления кода блокировки и т. д.? Если нет, то как насчет библиотек Boost?

EDIT:

Привет. Спасибо за первые отличные ответы. Думаю, одна из причин, по которой я думал, что это может быть встроенный был в том, что я думал исключительно в пространстве x86 и думал, что толчок/POP указателей должен быть атомарным действием на уровне инструкций.

Я не уверен, что моя первоначальная догадка верна или нет, но я думаю, это не обязательно так на всех платформах. Хотя, если вы работаете на x86, вы получаете атомарные толчки и всплывающие окна в стек, и если да, то это по существу делает его свободным от блокировки?

6 ответов


да: импульс.Нить отлично, и должно соответствовать вашим потребностям очень хорошо. (В наши дни многие говорят, что вы можете почти считать Boost встроенной функциональностью.)

по-прежнему нет класса, который вы могли бы использовать из коробки, но как только у вас есть примитивы синхронизации под рукой, действительно довольно просто реализовать свою собственную потокобезопасную оболочку, например,std::stack. Это может выглядеть примерно так (не все реализации метод...):

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;
}    

если вы новичок в C++, пожалуйста, узнайте о RAII. Это имеет отношение к делу, буст.Нить имеет классы "scoped lock", чтобы сделать его трудно стрелять себе в ногу, забыв отпустить замок.

если вы когда-нибудь окажетесь пишу такой код:

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

, тогда вы должны просто сказать " нет " и вместо этого пойти RAII (синтаксис не напрямую из Boost):

void doStuff() {
  scoped_lock lock;
  if (!condition) {
    reportError();
    return;
  }
  doStuffThatMayThrow();
  doMoreStuff();
}

дело в том, что когда scoped_lock объект выходит из области видимости, деструктор освобождает ресурс, в данном случае-замка. Это всегда будет происходить, независимо от того, выходите ли вы из области, создавая исключение или выполняя нечетное return заявление, которое ваш коллега тайком добавил в середине вашей функции, или просто достигнув конца функции.


текущий стандарт C++ вообще не касается потоковой передачи, поэтому ответ на ваш первый вопрос-нет. И вообще, строить блокировку в базовых структурах данных-плохая идея, потому что у них недостаточно информации для ее правильного и/или эффективного выполнения. Вместо этого блокировка должна выполняться в классах, использующих структуры данных - другими словами, в ваших собственных классах приложений.


AFAIK, нет встроенной поддержки на C++. Вам придется синхронизировать операции стека с помощью простого инструмента синхронизации. CriticalSection будет делать, если потоки принадлежат к тому же proceass в противном случае идут на мьютекс.


нет встроенного механизма для поддержки этого в C++ или в библиотеках Boost (Примечание: некоторые люди написали потокобезопасные стеки / etc. в стиле Boost). Вам придется заимствовать какой-то код или готовить в своей собственной синхронизации.

обратите внимание, что в вашем случае, вероятно, требуется один-writer multiple-reader guard (SWMRG), в котором несколько потоков записи могут получить доступ к стеку (но только один в данный момент времени) и в котором несколько читателей могут получить доступ к стек (многие в данный момент времени). У Рихтера есть эталонной реализации.


Если вы не хотите использовать блокировку, вам нужно использовать стопку без блокировки. На самом деле это не так сложно (без блокировки очередь сложнее). Вам нужен примитив compare-exchange для конкретной платформы, такой как InterlockedCompareExchange в Windows, но это не сложно абстрагировать.

см. здесь пример в C#:

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


Если вы работаете в Windows, SLIST реализует стек без блокировки (со структурами SLIST_HEADER & SLIST_ENTRY).

алгоритм реализован с использованием довольно тривиального push / pop односвязного стека списков с использованием взаимосвязанных функций. Единственным неочевидным элементом является приращение счетчика, чтобы избежать проблем ABA.