Namespaces
Variants

Atomic types

From cppreference.net

目录

语法

_Atomic ( 类型名称 ) (1) (自 C11 起)
_Atomic 类型名称 (2) (自 C11 起)
1) 用作类型说明符;这表示一个新的原子类型
2) 用作类型限定符;这表示 type-name 的原子版本。在此用法中,它可以与 const volatile restrict 混合使用,但与其他限定符不同的是, type-name 的原子版本可能具有不同的大小、对齐方式和对象表示形式。
类型名称 - 除数组或函数外的任意类型。对于 (1) 类型名称 也不能是原子类型或带有cv限定符的类型

头文件 <stdatomic.h> 定义了 众多便捷类型别名 ,从 atomic_bool atomic_uintmax_t ,这些别名简化了该关键字与内置类型及库类型的使用。

_Atomic const int* p1;  // p1 是指向原子常量整数的指针
const atomic_int* p2;   // 同上
const _Atomic(int)* p3; // 同上

如果编译器定义了宏常量 __STDC_NO_ATOMICS__ ,则不会提供关键字 _Atomic

说明

原子类型的对象是唯一不受 数据竞争 影响的对象;也就是说,它们可以被两个线程并发修改,或者被一个线程修改的同时被另一个线程读取。

每个原子对象都有其自身的 修改顺序 ,这是对该对象进行修改的全序。如果从某个线程的视角来看,某个原子对象 M 的修改 A 先于 同一原子对象 M 的修改 B 发生,那么在 M 的修改顺序中, A 将位于 B 之前。

请注意,尽管每个原子对象都有其自身的修改顺序,但不存在单一的总顺序;不同线程可能以不同顺序观察到对不同原子对象的修改。

所有原子操作保证具有四种一致性类型:

  1. 写-写连贯性 :若修改原子对象 M 的操作 A 先发生于 修改 M 的操作 B ,则 A M 的修改顺序中早于 B 出现。
  2. 读-读连贯性 :若原子对象 M 的值计算 A 先发生于 M 的值计算 B ,且 A M 的副作用 X 获取其值,则 B 计算得出的值要么是 X 存储的值,要么是 M 的副作用 Y 存储的值,其中 Y M 的修改顺序中晚于 X 出现。
  3. 读-写连贯性 :若原子对象 M 的值计算 A 先发生于 M 上的操作 B ,则 A M 的副作用 X 获取其值,其中 X M 的修改顺序中早于 B 出现。
  4. 写-读连贯性 :若原子对象 M 的副作用 X 先发生于 M 的值计算 B ,则计算 B X 或从在 M 的修改顺序中晚于 X 出现的副作用 Y 获取其值。

某些原子操作同时也是同步操作;它们可能具有额外的释放语义、获取语义或顺序一致语义。请参阅 memory_order

内置的 递增和递减运算符 以及 复合赋值运算符 是具有完全顺序一致排序的读-修改-写原子操作(如同使用 memory_order_seq_cst )。若需要更宽松的同步语义,可以使用 标准库函数 作为替代。

原子属性仅对 左值表达式 有意义。左值到右值的转换(模拟从原子位置到CPU寄存器的内存读取操作)会剥离原子性及其他限定符。

注释

访问原子结构体/联合体的成员是未定义行为。

库类型 sig_atomic_t 不提供线程间同步或内存排序,仅保证原子性。

volatile 类型不提供线程间同步、内存排序或原子性保证。

建议实现确保C语言中的 _Atomic ( T ) 表示与C++中 std :: atomic < T > 对每个可能类型 T 的表示保持一致。用于确保原子性和内存顺序的机制应当相互兼容。

关键词

_Atomic

示例

#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>
atomic_int acnt;
int cnt;
int f(void* thr_data)
{
    for (int n = 0; n < 1000; ++n)
    {
        ++cnt;
        ++acnt;
        // 对于此示例,宽松内存序已足够,例如:
        // atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed);
    }
    return 0;
}
int main(void)
{
    thrd_t thr[10];
    for (int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for (int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);
    printf("The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

可能的输出:

The atomic counter is 10000
The non-atomic counter is 8644

参考文献

  • C23 标准 (ISO/IEC 9899:2024):
  • 6.7.2.4 原子类型说明符 (p: TBD)
  • 7.17 原子操作 <stdatomic.h> (p: TBD)
  • C17 标准 (ISO/IEC 9899:2018):
  • 6.7.2.4 原子类型说明符 (p: 87)
  • 7.17 原子操作 <stdatomic.h> (p: 200-209)
  • C11 标准 (ISO/IEC 9899:2011):
  • 6.7.2.4 原子类型说明符 (p: 121)
  • 7.17 原子操作 <stdatomic.h> (p: 273-286)

参见

并发支持库
C++ 文档 关于 atomic