簡體   English   中英

atomic_thread_fence(memory_order_release) 與使用memory_order_acq_rel 有什么不同?

[英]Is atomic_thread_fence(memory_order_release) different from using memory_order_acq_rel?

cppreference.com 提供了關於std::atomic_thread_fence注釋(重點是我的):

atomic_thread_fence 比具有相同 std::memory_order 的原子存儲操作強加了更強的同步約束。

雖然原子存儲釋放操作可防止所有先前的寫入移動超過存儲釋放,但具有 memory_order_release 排序的 atomic_thread_fence 可防止所有先前寫入移動超過所有后續存儲

我理解這個注釋意味着std::atomic_thread_fence(std::memory_order_release)不是單向的,就像存儲釋放一樣。 這是一個雙向圍欄,可防止圍欄兩側的商店重新排序超過圍欄另一側的商店。

如果我理解正確的話,這個圍欄似乎做出了與atomic_thread_fence(memory_order_acq_rel)一樣的保證。 它是一個“向上”的柵欄,也是一個“向下”的柵欄。

std::atomic_thread_fence(std::memory_order_release)std::atomic_thread_fence(std::memory_order_acq_rel)之間是否存在功能差異? 或者區別僅僅是為了記錄代碼的目的?

與具有相同排序約束的原子操作相比,獨立柵欄強加了更強的排序,但這不會改變強制執行排序的方向。

Bot 一個原子釋放操作和一個獨立的釋放柵欄是單向的,但是原子操作相對於自身進行排序,而原子柵欄相對於其他存儲強加排序。

例如,具有釋放語義的原子操作:

std::atomic<int> sync{0};

// memory operations A

sync.store(1, std::memory_order_release);

// store B

這保證了 A(加載和存儲)的任何內存操作部分都不能(可見地)與原子存儲本身重新排序。 但它是單向的,並且沒有排序規則適用於在原子操作之后排序的內存操作; 因此,存儲 B 仍然可以通過 A 中的任何內存操作重新排序。

一個獨立的發布柵欄改變了這種行為:

// memory operations A

std::atomic_thread_fence(std::memory_order_release);

// load X

sync.store(1, std::memory_order_relaxed);

// stores B

這保證了 A 中的任何內存操作都不能(可見地)與在釋放柵欄之后排序的任何存儲重新排序。 在這里,到 B 的存儲不能再用 A 中的任何內存操作重新排序,因此,釋放柵欄比原子釋放操作更強。 但它也是單向的,因為來自 X 的負載仍然可以通過 A 中的任何內存操作重新排序。

區別很微妙,通常原子釋放操作比獨立的釋放柵欄更受歡迎。

獨立獲取柵欄的規則類似,不同之處在於它強制執行相反方向的排序並對負載進行操作:

// loads B

sync.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);

// memory operations A

在無存儲器操作可以獨立獲取籬笆之前進行測序任何負荷被重新排序。

具有std::memory_order_acq_rel排序的獨立柵欄結合了獲取柵欄和釋放柵欄的邏輯。

// memory operations A
// load A

std::atomic_thread_fence(std::memory_order_acq_rel);

// store B
//memory operations B

但是,一旦您意識到 A 中的存儲仍然可以使用 B 中的負載重新排序,這會變得非常棘手。應該避免使用 Acq/rel 柵欄,以支持常規原子操作,甚至更好的互斥鎖。

cppreference.com 在您引用的段落中犯了一些錯誤。 我在以下內容中強調了它們:

atomic_thread_fence 比具有相同 std::memory_order 的原子存儲操作強加了更強的同步約束。 雖然原子存儲釋放操作防止所有先前的寫入(應該是內存操作,即包括讀取和寫入)移動到存儲釋放(完整的句子應該是:存儲釋放操作本身),但具有 memory_order_release 排序的 atomic_thread_fence防止所有先前的寫入(應該是內存操作,即包括讀取和寫入)移過所有后續存儲。

解釋一下:

釋放柵欄相比,釋放操作實際上對相鄰操作施加的內存排序約束更少。 釋放操作只需要防止先前的內存操作被重新排序超過其自身,但釋放柵欄必須防止先前的內存操作被重新排序超過所有后續寫入。 由於這種差異,釋放操作永遠不能代替釋放柵欄。

這是從這里引用的。

這是我對以下文本意圖的解釋,我認為這就是意圖。 此外,就內存模型而言,這種解釋是正確的,但仍然很糟糕,因為它是一個不完整的解釋

雖然原子存儲釋放操作可防止所有先前的寫入移動超過存儲釋放,但具有memory_order_release排序的atomic_thread_fence可防止所有先前寫入移動超過所有后續存儲。

“存儲”與“寫入”的使用是有意的:

  • “store”,在這里,是指std::atomic<>對象上的存儲(不僅僅是對std::atomic<>::store的調用,還有等價於.store(value)或 RMW 原子操作的賦值);
  • “寫入”在這里是指任何內存寫入,無論是正常的(非原子的)還是原子的。

這是一個雙向圍欄,可防止圍欄兩側的商店重新排序超過圍欄另一側的商店。

不,你錯過了一個重要的區別,因為它只是隱含的; 以一種不清楚、太微妙的方式表達——不適合教科書!

它說釋放柵欄不是對稱的:先前的內存副作用,稱為“寫入”,受以下原子存儲操作的約束。

即使有這樣的澄清,它也是不完整的,所以這是一個糟糕的解釋:它強烈表明存在發布柵欄只是為了確保寫入(和僅寫入)完成 事實並非如此。

釋放操作就是我所說的:“我在那里完成了”信號。 它表示所有先前的內存操作已完成、完成、可見 重要的是要了解,不僅修改(可以通過查看內存狀態檢測到)是有序的,內存上的所有內容都需要是.

許多關於線程原語的文章都存在這種缺陷。

暫無
暫無

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

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