c - 访问静态初始化变量时是否应该使用屏障?
问题描述
在我的函数中,我有以下两行代码:
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_release
或者acquire
如果其他线程与我们竞争,那么作为确保静态初始化在原子 RMW 之前完成的一种方式是没有意义的。从其他线程的 POV 中 控制我们操作的可见性顺序。我很确定语言规则只要求 RMW 发生在所有static foo = bar
暗示的事情之后(无论是在执行初始化还是在必要时等待它),因为顺序排序。 如果您考虑非原子情况,其他任何事情都没有任何意义。您不能让其他线程读取未初始化的变量。
(请注意,C 仅支持函数范围的静态变量的非常量初始值设定项。只有 C++ 支持全局变量。)
顺便说一句,几乎没有理由使用已弃用/遗留的GNU C__sync
内置函数:手册说:它们不应该用于应该使用“__atomic”内置函数的新代码。
内置函数的第三个参数__sync
不是内存顺序,它是 GCC 忽略的“受内存屏障保护的变量的可选列表”。它__atomic_fetch_add
采用内存顺序参数。
或者在大多数情况下更好,C11使用https://en.cppreference.com/w/c/atomic/atomic_fetch_add<stdatomic.h>
对其进行_Atomic static uint64_t static_index = 0;
修改
atomic_fetch_add_explicit(&static_index, 1, memory_order_relaxed);
(或者,如果您愿意,idx = static_index++;
但默认为 seq_cst,因此对于非 x86 ISA 的编译效率会降低。)
您不需要volatile _Atomic
,因此您可以删除volatile
类型限定符。由于 C11 / C++11 可用,现在通常不建议使用volatile
手动放松原子,但如果你这样做,那么简单的加载/存储访问有点像带有 mo_relaxed 的 _Atomic。volatile
推荐阅读
- flutter - 使用 http 请求获取嵌套 Json 返回 null
- mysql - 与node js应用程序连接时如何解决mysql错误号1064?
- kotlin - 相同类的 Kotlin 密封类
- java - Java 运行时出现终端错误,无法识别类文件版本 > 52.0
- docker - 使用 aws-sam-cli 时出错 | 未能在公司代理后面拉取 docker 映像
- html - 创建动态网格时防止 div 增长/拉伸
- ruby-on-rails - 插入 html_tag 类属性 - 格式错误
- c# - VSTO Outlook 插件 - 上传大文件会阻止 Outlook 直到进程完成
- ios - 测试 iOS / MacOS X 的应用内购买和测试帐户的双因素身份验证
- .net - VSCode + 非核心网络框架