gpt4 book ai didi

c - 我应该在访问静态初始化变量时使用屏障吗?

转载 作者:行者123 更新时间:2023-12-03 12:58:47 25 4
gpt4 key购买 nike

在我的函数中,我有以下两行代码:

static volatile uint64_t static_index = 0;
const uint64_t index = __sync_fetch_and_add(&static_index, 1, __ATOMIC_RELAXED);

如您所见, static_index在线程之间共享,而 index是每线程。
我担心的是,静态初始化可能会因使用此变量而重新排序,但我不确定这是否可以应用于静态(一次)初始化的变量。

在这种情况下,__ATOMIC_RELAXED 是否足以避免重新排序?或者我应该使用 __ATOMIC_RELEASE甚至 __ATOMIC_SEQ_CST这里?

我感谢任何帮助,谢谢。

最佳答案

您的静态初始值设定项是一个编​​译时常量,因此您可以(至少在实践中)依靠已经持有 0 的静态存储空间。当您的流程开始时。
(具体来说,它将在此处的 BSS 中。非零常量意味着它位于 .data 部分。)

我很确定它对于非常量初始值设定项也是安全的。
对于具有非常量初始值设定项的函数作用域静态变量,进入函数的第一个线程运行初始值设定项。编译器通常使用保护变量。快速情况(已初始化)涉及对该保护变量的获取加载,以检查静态变量是否已被初始化。否则,原子 RMW 确保恰好有 1 个线程运行初始化程序,而其他线程则等待它。
但是撇开实现细节不谈:我没有仔细检查标准对静态变量的说明。但是在执行初始化的线程中,static volatile foo = x在它上面的RMW之前清楚地排序,因此保证发生在之前。
在其他线程中,问题是它们是否可以通过静态初始化重新排序。我认为答案一定是否定的,否则你会在没有原子内置函数的情况下读取或写入数据竞争 UB。
在一个帖子内,您可以查看static foo = non_const;确保 foo被初始化。即使我们不是进行初始化的线程。memory_order_releaseacquire如果其他线程正在与我们竞争,那么作为确保在原子 RMW 之前完成静态初始化的方法是没有意义的。这控制了我们操作从其他线程的 POV 的可见性顺序。 我很确定语言规则只要求 RMW 发生在 static foo = bar 之后。由于序列排序,暗示(无论是进行初始化还是在必要时等待它)。 如果您考虑非原子情况,其他任何事情都没有任何意义。您不能让其他线程读取未初始化的变量。
(请注意,C 仅支持函数作用域静态变量的非常量初始值设定项。只有 C++ 支持全局变量。)

顺便说一句,几乎没有理由使用已弃用/遗留的 GNU C __sync builtins :手册说:它们不应该用于应该使用 ‘__atomic’ builtins instead 的新代码。 .
__sync 的第三个参数内置函数不是内存顺序 ,它是 GCC 忽略的“受内存屏障保护的变量的可选列表”。它是 __atomic_fetch_add,它采用内存顺序参数。

或者在大多数情况下更好,C11 <stdatomic.h>_Atomic static uint64_t static_index = 0;并用 https://en.cppreference.com/w/c/atomic/atomic_fetch_add 修改它

atomic_fetch_add_explicit(&static_index, 1, memory_order_relaxed);
(或者,如果您愿意, idx = static_index++; 但默认为 seq_cst,因此对于非 x86 ISA 的编译效率会降低。)
你不需要 volatile _Atomic所以你可以放下 volatile类型限定符。使用 volatile手卷松弛原子是 generally not recommended现在 C11/C++11 可用,但如果你这样做了,那么简单的加载/存储访问 volatile有点像 _Atomic 和 mo_relaxed。

关于c - 我应该在访问静态初始化变量时使用屏障吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59985569/

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