簡體   English   中英

C++ memory_order_acquire/release問題

[英]C++ memory_order_acquire/release questions

最近了解了 c++ 六個 memory 訂單,對memory_order_acquirememory_order_release感到很困惑,下面是來自 cpp 的示例:

#include <thread>
#include <atomic>
#include <cassert>
 
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
 
void write_x() { x.store(true, std::memory_order_seq_cst); }
void write_y() { y.store(true, std::memory_order_seq_cst); }
 
void read_x_then_y() {
     while (!x.load(std::memory_order_seq_cst));

     if (y.load(std::memory_order_seq_cst)) 
         ++z;
}
 
void read_y_then_x() {
     while (!y.load(std::memory_order_seq_cst));

     if (x.load(std::memory_order_seq_cst))
        ++z;
}
 
int main() {
    std::thread a(write_x);
    std::thread b(write_y);
    std::thread c(read_x_then_y);
    std::thread d(read_y_then_x);

    a.join(); b.join(); c.join(); d.join();

    assert(z.load() != 0);  // will never happen
}

在 cpp 參考頁面中,它說:

此示例演示了需要順序排序的情況。

任何其他排序都可能觸發斷言,因為線程 c 和 d 可能以相反的順序觀察原子 x 和 y 的變化。

所以我的問題是為什么不能在這里使用memory_order_acquirememory_order_release memory_order_acquire 和 memory_order_release 提供什么語義?

一些參考資料: https://en.cppreference.com/w/cpp/atomic/memory_order https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync

順序一致性提供了所有順序一致操作的單一總順序 因此,如果您在線程 A 中有一個順序一致的存儲,並且在線程 B 中有一個順序一致的加載,並且存儲在加載之前排序(按所述單個總順序),那么 B 會觀察 A 存儲的值。所以基本上是順序一致性保證存儲對其他線程“立即可見”。 發行商店提供此保證。

正如 Peter Cordes 正確指出的那樣,“立即可見”一詞相當不精確。 “可見性”源於這樣一個事實,即所有 seq-cst 操作都是完全有序的,並且所有線程都遵守該順序。 由於存儲和加載是完全排序的,因此在執行后續加載(以單個總順序)之前,存儲的值變得可見。

不同線程中的獲取/釋放操作之間不存在這樣的總順序,因此不存在可見性保證。 只有在獲取操作觀察到釋放操作的值時,操作才會被排序,但不能保證釋放操作的值何時對執行獲取操作的線程可見。

讓我們考慮一下如果我們在這個例子中使用獲取/釋放會發生什么:

void write_x() { x.store(true, std::memory_order_release); }
void write_y() { y.store(true, std::memory_order_release); }
 
void read_x_then_y() {
     while (!x.load(std::memory_order_acquire));

     if (y.load(std::memory_order_acquire)) 
         ++z;
}
 
void read_y_then_x() {
     while (!y.load(std::memory_order_acquire));

     if (x.load(std::memory_order_acquire))
        ++z;
}
 
int main() {
    std::thread a(write_x);
    std::thread b(write_y);
    std::thread c(read_x_then_y);
    std::thread d(read_y_then_x);

    a.join(); b.join(); c.join(); d.join();

    assert(z.load() != 0);  // can actually happen!!
}

由於我們無法保證可見性,因此線程c可能會觀察到x == truey == false ,而同時線程d可能會觀察到y == truex == false 所以兩個線程都不會增加z並且斷言會觸發。

For more details about the C++ memory model I can recommend this paper which I have co-authored: Memory Models for C/C++ Programmers

在將信息從一個線程傳遞到另一個線程時,您可以使用 aquire/release - 這是最常見的情況。 不需要對此有順序要求。

在這個例子中,有一堆線程。 兩個線程進行寫操作,而第三個線程粗略地測試x是否在y之前准備好,第四個線程測試y是否在x之前准備好。 從理論上講,一個線程可能會觀察到xy之前被修改,而另一個線程會看到yx之前被修改。 不完全確定它的可能性有多大。 這是一個不常見的用例。

編輯:您可以可視化該示例:假設每個線程都在不同的 PC 上運行,並且它們通過網絡進行通信。 每對 PC 之間都有不同的 ping。 這里很容易舉一個例子,其中不清楚哪個事件首先發生在xy上,因為每台 PC 會看到事件以不同的順序發生。

我不確定這種效果可能會發生在哪些架構上,但有一些復雜的架構,其中兩個不同的處理器結合在一起。 當然,處理器之間的通信比每個處理器的內核之間的通信要慢。

暫無
暫無

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

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