簡體   English   中英

C ++:重新排序原子庫(發布)和加載(獲取)

[英]C++: Reordering atomic store (release) and load (acquire)

我編寫了以下代碼,它有點像一個編寫器和一個閱讀器的同步隊列。 從不超過1位讀者和1位作家。

作者反復調用maybePublish ,它被設計為無鎖。 相反,讀者使用spinUntilFreshAndFetch 它通過原子變量通知它想要下一個非常新鮮的項目。 存儲之后,它會旋轉原子變量,等待編寫器將其設置回0,之后它可以獲取共享對象並將其放入自己的副本中。

class Shared {
public:
    void maybePublish(const Item &item) {
        if (mItemSync.load(std::memory_order_acquire) == 1) {
            mItem = item;
            mItemSync.store(0, std::memory_order_release);
        }
    }

    void spinUntilFreshAndFetch(Item *copy) {
        mItemSync.store(1, std::memory_order_release);  // A
        while (mItemSync.load(std::memory_order_acquire) != 0) {  // B
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
        *copy = mItem;
    }
private:
    Item mItem;
    std::atomic_int32_t mItemSync = 0;
};

我的擔心是關於A行和B行。我在標准中看不到任何不允許交換這些行的內容。 該標准保證發行不會浮動在收購之上,但並不意味着收購不能浮動在發行之上。

另外,我擔心它可能會被優化。 例如,編譯器是否可以假設,在B處, mItemSync除了1(來自A行) mItemSync不能是其他任何東西,並將其轉換為無限循環?

根據我看到的教程,如果我使用std::memory_order_seq_cst而不能重新排序A和B. 我應該這樣做嗎?

謝謝你的建議!

該計划很好。

原子意味着:編譯器不能在單個線程上重新排序它們,保證波動性(因此沒有無限循環)和原子性(操作是不可分割的)。

獲取和釋放語義意味着: 如果獲取操作觀察到釋放操作的副作用,則在發布之前完成任何操作。

如果我們將釋放表示為}並獲得為{ 括號內的任何內容都不能按其語義向外移動。 你的兩個線程看起來就像

reader             } {  {  {{   {   { R
writer {    {   {{    {  W       }
                   ^  ^          ^  ^
                   1  2          3  4
  1. 作者首先反復嘗試發布和獲取,直到讀者發布才會失敗。

  2. 作者將在讀者發布后的某個時間獲得。

  3. 與此同時,讀者反復嘗試獲取,在作者發布之前也會失敗。

  4. 讀者獲得了。

請注意這4個操作必須按此順序進行。 寫入mItem保證在23之間,讀取必須在4之后進行。 結合平鋪這兩個線程仍保留此屬性意味着程序正常。

暫無
暫無

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

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