在应用于 std::memory_order_seq_cst 之前,C++23 是如何发生的?

问题描述 投票:0回答:1

考虑以下 C++23 程序:

#include <atomic>
#include <cassert>
#include <thread>

#define LOAD_ORDER std::memory_order_seq_cst
#define STORE_ORDER std::memory_order_seq_cst

std::atomic_int v[2]{0, 0};
int r[2];

void
f(int i)
{
  v[i].store(1, STORE_ORDER);
  r[i] = v[1-i].load(LOAD_ORDER);
}

int
main()
{
  {
    std::jthread t0(f, 0);
    std::jthread t1(f, 1);
  }
  assert(r[0] || r[1]);
  return 0;
}

一个线程将 1 存储到

v[0]
,然后加载
v[1]
。 同时,另一个线程将 1 存储到
v[1]
,然后加载
v[0]
。 我想确保至少一次加载返回 1。我的问题:

  1. 在较高级别上,顺序一致的操作无法重新排序,因此这应该可行。 但是,如何仅使用语言规范中的之前排序、同步、线程间发生在之前以及可见副作用概念来证明上述代码是正确的? 例如,一家商店似乎不会出现在另一家商店之前。

  2. 如果我将

    LOAD_ORDER
    更改为
    std::memory_order_relaxed
    并将
    STORE_ORDER
    保留为
    std::memory_order_seq_cst
    ,代码仍然正确吗? 放松
    STORE_ORDER
    但不施加负荷的反面呢?

  3. 我可以设置

    LOAD_ORDER
    STORE_ORDER
    并且仍然保证断言不会失败的最宽松或最有效的值是多少?

假设所有变量都位于不同的缓存行上——我只是没有添加填充以使示例更简单。 还假设没有消耗操作,所以我们可以忽略之前发生的弱/强/普通之间的区别。

c++ language-lawyer memory-model relaxed-atomics
1个回答
0
投票

所有具有顺序一致性的原子操作都以全局总顺序发生。

因此,您有存储到

v[0]
v[1]
,我们称它们为
S0
S1
,并类似地加载
L0
L1

这个顺序需要与sequenced-with关系一致。在第一个线程中,

S0
L1
之前排序,在第二个线程中,
S1
L2
之前排序,即
S0 < L1
S1 < L0

在全局总顺序中,您有

L0 < L1
L1 < L0

L0 < L1
S1 < L0
一起意味着
S1 < L1
,即
L1
将加载
1
。对于
L1 < L0
,您同样会得到
L0
将加载
1

无论哪种方式,断言都得到满足。


如果削弱内存排序,则断言将不再得到保证。

© www.soinside.com 2019 - 2024. All rights reserved.