命名空间
变体
操作

原子类型

来自 cppreference.com
< c‎ | 语言

内容

[编辑] 语法

_Atomic ( 类型名 ) (1) (自 C11 起)
_Atomic 类型名 (2) (自 C11 起)
1) 用作类型说明符;这指定了一个新的原子类型
2) 用作类型限定符;这指定了 类型名 的原子版本。在这种情况下,它可以与 常量易变的限定 混合使用,尽管与其他限定符不同,类型名 的原子版本可能具有不同的大小、对齐方式和对象表示。
类型名 - 除数组或函数以外的任何类型。对于 (1)类型名 也不得为原子或 cvr 限定的。

头文件 <stdatomic.h> 定义了 37 个方便的类型别名,从 atomic_boolatomic_uintmax_t,它们简化了使用此关键字与内置类型和库类型的操作。

_Atomic const int * p1;  // p is a pointer to an atomic const int
const atomic_int * p2;   // same
const _Atomic(int) * p3; // same

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

[编辑] 解释

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

每个原子对象都有与其关联的修改顺序,它是对该对象进行的修改的总顺序。如果从某个线程的角度来看,对某个原子 M 的修改 A发生之前 对同一个原子 M 的修改 B,那么在 M 的修改顺序中,A 发生在 B 之前。

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

对于所有原子操作,保证有四种一致性

  • 写写一致性:如果修改原子对象 M 的操作 A 发生之前 修改 M 的操作 B,那么 A 出现在 M 的修改顺序中 B 之前。
  • 读读一致性:如果对原子对象 M 的值计算 A 发生在对 M 的值计算 B 之前,并且 A 从对 M 的副作用 X 获取其值,那么 B 计算的值要么是 X 存储的值,要么是 X 存储的值对 M 的副作用 Y,其中 Y 出现在 M 的修改顺序中 X 之后。
  • 读写一致性:如果对原子对象 M 的值计算 A 发生之前 对 M 的操作 B,那么 A 从对 M 的副作用 X 获取其值,其中 X 出现在 B 的修改顺序中 B 之前。
  • 写读一致性:如果对原子对象 M 的副作用 X 发生之前 对 M 的值计算 B,那么评估 B 从 X 或从出现在 X 之后的副作用 Y 获取其值 M 的修改顺序。

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

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

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

[编辑] 注意

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

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

易变的 类型不提供线程间同步、内存排序或原子性。

建议实现确保 _Atomic(T) 在 C 中的表示与 std::atomic<T> 在 C++ 中的表示相同对于每种可能的类型 T。用于确保原子性和内存排序的机制应该兼容。

[编辑] 关键字

_Atomic

[编辑] 示例

#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
 
atomic_int acnt;
int cnt;
 
int f(void* thr_data)
{
    for(int n = 0; n < 1000; ++n) {
        ++cnt;
        ++acnt;
        // for this example, relaxed memory order is sufficient, e.g.
        // 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

[编辑] 参考文献

  • 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 文档 用于 线程 (并发支持库)
C++ 文档 用于 原子