簡體   English   中英

boost vs std原子序列一致性語義

[英]boost vs std atomic sequential consistency semantics

我想寫一個C ++無鎖對象,其中有許多記錄器線程記錄到一個大的全局(非原子)環形緩沖區,偶爾的讀取器線程想要盡可能多地讀取緩沖區中的數據。 我最終得到了一個全局原子計數器,記錄器獲取要寫入的位置,每個記錄器在寫入之前以原子方式遞增計數器。 讀者嘗試讀取緩沖區和per-logger本地(原子)變量,以了解特定緩沖區條目是否忙於由某個記錄器寫入,以避免使用它們。

所以我必須在純讀者線程和許多編寫器線程之間進行同步。 我覺得問題可以在不使用鎖的情況下解決,我可以依靠“發生后”關系來確定我的程序是否正確。

我已經嘗試過輕松的原子操作,但它不會起作用:原子變量存儲是釋放和負載被獲取,並且保證是某些獲取(及其后續工作)總是“發生在”某些發布之后(及其之前的工作) )。 這意味着讀者線程(完全沒有存儲)無法保證在讀取緩沖區之后“發生”某些事情,這意味着我不知道某些記錄器是否覆蓋了部分緩沖區線程正在讀它。

所以我轉向順序一致性。 對我來說,“原子”是指Boost.Atomic,其中順序一致性概念有一個“模式” 記載

通過Boost.Atomic協調線程的第三種模式使用seq_cst進行協調:如果......

  1. thread1執行操作A,
  2. thread1隨后用seq_cst執行任何操作,
  3. thread1隨后執行操作B,
  4. thread2執行操作C,
  5. thread2隨后用seq_cst執行任何操作,
  6. thread2隨后執行操作D,

然后要么“A發生在D之前”或“C發生在B之前”。

請注意,第二行和第五行表示“任何操作”,而不說是否修改任何內容或操作內容。 這提供了我想要的保證。

所有人都很高興,直到我看到Herb Sutter題為“原子<> Weapnos”的談話。 他暗示的是seq_cst只是一個acq_rel,具有一致的原子商店排序的額外保證。 我轉向cppreference.com ,它有類似的描述。

所以我的問題:

  1. C ++ 11和Boost Atomic是否實現了相同的內存模型?
  2. 如果(1)為“是”,是否意味着Boost所描述的“模式”以某種方式隱含在C ++ 11內存模型中? 怎么樣? 或者它是否意味着cppreference中的Boost或C ++ 11的文檔是錯誤的?
  3. 如果(1)是“否”,或者(2)是“是,但是Boost文檔不正確”,有沒有辦法在C ++ 11中實現我想要的效果,即保證(后續的工作) )一些原子存儲發生在(前面的工作)一些原子載荷之后?

我在這里沒有看到答案,所以我再次在Boost用戶郵件列表中詢問。 我也沒有看到任何答案(除了建議調查Boost lockfree),所以我計划問Herb Sutter(無論如何都沒有回答)。 但在此之前,我用Google搜索了“C ++內存模型”。 在閱讀Hans Boehm( http://www.hboehm.info/c++mm/ )的頁面后,我可以回答我自己的大部分問題。 我用Google搜索了一下,這次是為了“C ++數據競賽”,並在Bartosz Milewski的一頁上登陸( http://bartoszmilewski.com/2014/10/25/dealing-with-benign-data-races-the- c-way / )。 然后我可以回答更多我自己的問題。 不幸的是,鑒於這些知識,我仍然不知道如何做我想做的事情。 也許我想做的事實上在標准C ++中實際上是無法實現的。

我的第一部分問題是:“C ++ 11和Boost.Atomic是否實現了相同的內存模型?” 答案主要是“是”。 問題的第二部分:“如果(1)是'是',它是否意味着Boost描述的”模式“在某種程度上暗示了C ++ 11內存模型?” 答案是,是的。 “怎么樣?” 通過此處的證據( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2392.html )回答。 從本質上講,對於無數據競爭的程序,添加到acq_rel的一點點就足以保證seq_cst所需的行為。 所以這兩個文檔雖然可能令人困惑,但都是正確的。

現在真正的問題是:雖然(1)和(2)都得到“是”答案,但我的原始程序是錯誤的! 我忽略了(實際上,我不知道)C ++的一個重要規則:具有數據競爭的程序具有未定義的行為(而不是“未指定”或“實現定義”行為)。 也就是說,只有當我的程序完全沒有數據爭用時,編譯器才會保證程序的行為。 沒有鎖定,我的程序包含數據競爭:純讀取器線程可以隨時讀取,即使在記錄器線程忙於寫入時也是如此。 這是“未定義的行為”,規則說計算機可以做任何事情(“火災”規則)。 要修復它,必須使用我前面提到的Bartosz Milewski頁面中的想法,即更改環形緩沖區以僅包含原子內容,以便編譯器知道它的順序很重要,不能與操作重新排序標記為需要順序一致性。 如果需要開銷最小化,可以使用放松的原子操作寫入它。

不幸的是,這也適用於讀者線程。 我不能只是“memcpy”整個內存緩沖區。 相反,我還必須使用寬松的原子操作來一個接一個地讀取緩沖區。 這會導致性能下降,但我實際上別無選擇。 幸運的是,對我來說,自卸車的性能對我來說並不重要:無論如何它很少運行。 但是,如果我想要“memcpy”的表現,我會得到一個“沒有解決方案”的答案:C ++沒有提供“我知道有數據競爭的語義,你可以在這里向我返回任何內容,但不要搞砸我的程序”。 要么你確保沒有數據競爭並支付成本來定義一切,或者你有一個數據競爭,並允許編譯器讓你入獄。

暫無
暫無

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

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