glibc 手册中的这段文字仅与以下内容相关:volatile sig_atomic_t
安全线程与其信号处理程序之间。他们保证volatile int
在 GNU 系统中也将这样工作。请注意您省略的上下文:
为了避免不确定性打断访问变量时,您可以使用特定的数据类型,该数据类型的访问始终是原子的:sig_atomic_t。保证在一条指令中读取和写入此数据类型,因此无法处理程序在访问的“中间”运行。
这并没有说明原子性。其他线程或并发。中断处理程序接管 CPU 并运行instead你的代码。任何时候都是异步的,但主线程及其信号处理程序不会同时运行(尤其是在不同的 CPU 内核上)。
对于线程来说,原子性是不够的,还需要保证能见度线程之间(你可以从_Atomic int
with memory_order_relaxed
),有时您可以通过更强的记忆顺序来获得排序。
请参阅前几部分为什么自然对齐变量的整数赋值在 x86 上是原子的? https://stackoverflow.com/questions/36624881/why-is-integer-assignment-on-a-naturally-aligned-variable-atomic-on-x86/36685056#36685056有关为什么 C 类型的宽度在您正在编译的目标上自然是原子的的更多讨论是not足够做任何事情。
有时您还需要 RMW 原子性,例如执行atomic_fetch_add 的能力,这样如果 1000 个这样+=1
操作发生在多个线程中,总结果如下+=1000
。为此,您绝对需要编译器支持(或内联汇编),例如 C11_Atomic int
. num++ 对于“int num”可以是原子的吗? https://stackoverflow.com/questions/39393850/can-num-be-atomic-for-int-num/39396999#39396999
保证int
是原子手段atomic_int
应该总是无锁的,而且便宜,但这绝对不意味着简单int
对于线程之间共享的数据来说是远程安全的。这就是数据竞争 UB,即使您使用像 GNU C 这样的内存屏障asm("" ::: "memory")
为了尝试让编译器不将值保留在寄存器中,您的代码可以通过许多有趣的方式进行中断,这些方式比一些明显的中断机制更微妙。看谁害怕一个糟糕的优化编译器? https://lwn.net/Articles/793253/在 LWN 上了解有关在 Linux 内核中执行此操作的一些注意事项,他们在其中使用volatile
为了原子性。
(有趣的事实:GNU C 至少事实上提供了纯加载和纯存储原子性volatile int64_t
在 64 位机器上,与普通的不同int64_t https://stackoverflow.com/questions/71866535/which-types-on-a-64-bit-computer-are-naturally-atomic-in-gnu-c-and-gnu-c-m/71867102#71867102。 ISO C 甚至不能保证这一点volatile
,这就是为什么 Linux 内核依赖于用 GCC 或 clang 编译的原因。)