gpt4 book ai didi

c++ - 在这种情况下,带有 memory_order_seq_cst 的原子读操作读取哪个值?

转载 作者:行者123 更新时间:2023-12-03 21:40:26 26 4
gpt4 key购买 nike

我已经阅读了 c++11 标准中关于内存排序的章节,但对规则感到困惑。根据C++11标准(ISO/IEC JTC1 SC22 WG21 N3690),29.3 3,据说:

There shall be a single total order S on all memory_order_seq_cst operations, consistent with the “happens before” order and modification orders for all affected locations, such that each memory_order_seq_cst operation B that loads a value from an atomic object M observes one of the following values:
— the result of the last modification A of M that precedes B in S, if it exists, or
— if A exists, the result of some modification of M in the visible sequence of side effects with respect to B that is not memory_order_seq_cst and that does not happen before A, or
— if A does not exist, the result of some modification of M in the visible sequence of side effects with respect to B that is not memory_order_seq_cst.



因此,请考虑以下情况:

有4个原子操作 A , , C , D .从代码:
  • 它们都是对同一个原子变量
  • 的操作
  • A 是任意顺序的写操作(可以放宽)
  • C 是使用 memory_order_seq_cst 的写操作
  • D 是带有 memory_order_seq_cst
  • 的读操作
  • A 是发生在 之前的最后一次写操作D
  • A , , C 相互之间没有happens-before关系。
  • D , , C 相互之间没有happens-before关系。

  • 考虑发生以下顺序的执行:
  • C 出现在 之前D 在 memory_order_seq_cst 操作的单个总顺序中
  • 此变量的修改顺序显示为 A -> -> C

  • 这是可能的代码
    using namespace std;

    atomic_bool go(false);
    atomic_int var(0);

    void thread1()
    {
    while (!go) {}
    var.store(1, memory_order_relaxed); // A
    this_thread::yield();
    cout << var.load(memory_order_seq_cst) << endl; // D
    }

    void thread2()
    {
    while (!go) {}
    var.store(2, memory_order_seq_cst); // C
    }

    void thread3()
    {
    while (!go) {}
    var.store(3, memory_order_relaxed); // B
    }

    int main() {
    thread t1(thread1);
    thread t2(thread2);
    thread t3(thread3);
    go = true;
    t1.join();
    t2.join();
    t3.join();
    }

    是否可以进行读操作 D 将读取操作 写入的值乙 , 给定 var 的 A,B,C 修改顺序?

    如果不可能,什么规则排除了这种可能性?

    如果可能,这意味着 memory_order_seq_cst可以读取最后一个“写在”之前的值 memory_order_seq_cst写。这是 C++ 标准中的“错误”,还是在并非一切都是 seq_cst 时有意设计的?

    最佳答案

    在这种情况下,D 可能从 A、B 或 C 中读取。

    考虑一个具有四个节点的图:A、B、C 和 D。
    和边(sc:顺序一致(总)排序(C --sc--> D),sb:排序之前/发生在之前(A --sb--> D),mo:修改顺序(A --mo- -> B --mo--> C),以及 rf: Read From (? --rf--> D))。

    图中的 rf 边与 C++ 内存模型不一致的原因有两个:因果关系和因为您无法从隐藏的视觉副作用中读取。

    如果暂时忽略 sc 边,那么 - 只有一个原子变量,图上唯一的因果限制是没有涉及 rf 边和(有向)sb 边的循环(这是来自 my research 的结果) .在这种情况下,甚至不存在这样的循环,因为您只有一个 rf 边——因此,没有任何理由无法从三个写入中的任何一个中读取。

    但是,您指定了确切的修改顺序(恕我直言,这并不重要,您应该只对程序的可能结果感兴趣)以及一个 sc 边缘。我们仍然必须调查这些是否与三个可能的 rf 边缘中的每一个兼容,以从隐藏的视觉副作用中读取。

    请注意,如果一个给定的 rf 边引入了一个同步,如果它的写节点是释放并且读节点是获取; sc 是释放/获取,因此后者为真,前者仅在从节点 C 读取时才成立。然而,同步意味着永远不会超过(按修改顺序)写入之前的所有内容必须发生在读取之后的所有内容之前;并且读完之后就什么都没有了,所以整个同步就无所谓了。

    此外,口述的修改顺序 (A --mo--> B --mo--> C) 与口述的总 sc 排序 (C --sc--> D) 并非因果不一致,因为 D 是读取而不是修改顺序子图的一部分。唯一不允许的(因为因果关系)是涉及 sc 和 mo 边的有向循环。

    现在,作为一个实验,假设我们使节点 A 也是 sc。然后我们需要将 A 放在总排序中,因此要么 A --sc--> C --sc--> D,C --sc--> A --sc--> D 或 C --sc- -> D --sc--> A,但我们有 A --mo--> C,所以后两者是不允许的(会导致(因果)循环),唯一可能的顺序是:A --sc- -> C --sc--> D. 现在不能再从 A 中读取,因为这会导致以下子图:

    A --sc--> C
    | /
    | /
    | /
    rf sc
    | /
    | /
    | /
    v v
    D

    并且 C 中的写入将始终覆盖 A 在被 D 读取之前写入的值(也就是,A 是 D 的隐藏视觉副作用)。

    如果 A 不是 sc(如原始问题中的情况),则仅在以下情况下才禁止此 rf(因为隐藏 vse)
    A --hb--> C
    | /
    | /
    | /
    rf sc
    | /
    | /
    | /
    v v
    D

    其中“hb”代表 Happens-Before(出于同样的原因;然后 A 是 D 的隐藏视觉副作用,因为 C 将始终在 D 读取之前覆盖 A 写入的值)。

    然而,在最初的问题中,线程 1 和 2 之间没有发生之前的事件,因为这样的同步需要两个线程之间的另一个 rf 边(或栅栏或任何会导致额外同步的东西)。

    最后,是的,这是预期的行为,而不是标准中的错误。

    编辑

    引用您引用的标准:

    — the result of the last modification A of M that precedes B in S, if it exists, or



    这里的A是你的C,这里的B是你的D。这里提到的A确实存在,即节点C(C --sc--> D)。所以这一行表示可以读取节点 C 写入的值。

    — if A exists, the result of some modification of M in the visible sequence of side effects with respect to B that is not memory_order_seq_cst and that does not happen before A, or



    同样,这里的 A 是你的 C,它存在。那么“在可见的副作用序列中对 M (var) 进行一些修改的结果不是 memory_order_seq_cst”就是你的 A。正如我们已经确定的那样,你的 A 之前不会发生你的 C(他们的 A)。因此,这表示可以读取从 A 写入的值。

    — if A does not exist, the result of some modification of M in the visible sequence of side effects with respect to B that is not memory_order_seq_cst.



    这在此处无关紧要,并且仅适用于在 B(您的 D)之前的 M(var)的总排序 S 中没有写入的情况。

    关于c++ - 在这种情况下,带有 memory_order_seq_cst 的原子读操作读取哪个值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44448530/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com