簡體   English   中英

線程安全的C ++堆棧

[英]Thread-safe C++ stack

我是C ++的新手,正在編寫一個多線程的應用程序,不同的編寫器將把對象推到堆棧上,讀者將它們從堆棧中拉出來(或者至少將指針推到一個對象上)。

是否有內置於C ++中的結構可以在不添加鎖定代碼等的情況下處理此問題? 如果沒有,Boost庫怎么樣?

編輯:

你好。 感謝最初的好答案。 我想我認為這可能是內置的一個原因是我純粹在x86空間思考並且認為指針的PUSH / POP應該是指令級別的原子動作。

我不確定我最初的預感是否正確,但我想在所有平台上都不一定如此。 雖然如果在x86上運行,你是否會將原子PUSH和POP發送到堆棧中,如果是這樣,這實際上是否使它無鎖?

是的: Boost.Thread很棒,應該很好地滿足你的需求。 (現在,很多人都說你幾乎可以將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 與此案相關,Boost.Thread有“范圍鎖定”類,因為忘記釋放鎖定很難讓自己在腿上射擊。

如果您發現自己編寫的代碼如下:

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會執行,否則為Mutex。

在C ++和Boost庫中都沒有內置機制來支持它(注意:有些人在Boost風格中編寫了線程安全堆棧等 )。 您必須借用一些代碼或在自己的同步中烹飪。

請注意,您的情況可能需要單寫入器多讀取器保護(SWMRG),其中多個寫入器線程可以訪問堆棧(但在給定時間點只能訪問一個)並且多個讀取器可以訪問堆棧(許多位於給定的時間點)。 Richter有參考實現

如果您不想使用鎖定,則需要使用無鎖堆棧。 這實際上並不那么難(無鎖隊列更難)。 您確實需要特定於平台的比較交換原語,例如Windows上的InterlockedCompareExchange,但這並不難以抽象。

請參閱此處以獲取C#中的示例:

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

如果您在Windows上運行,SLIST將實現無鎖堆棧(具有SLIST_HEADER & SLIST_ENTRY結構)。

該算法使用互鎖函數使用相當簡單的推/彈單鏈接列表堆棧來實現。 唯一不明顯的項目是計數器增量以避免ABA問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM