cpp之std::atomic

std::atomic

原子类型是对数据的一种封装,可以防止数据竞争,达到同步多线程的内存访问的目的。对该变量的读写是原子的。

1
#include <atomic>

使用前需要包含头文件。该头文件中主要包含两个类:atomic 和 atomic_flag。本文主要讲解 std::atomic。

构造函数和赋值

1
2
3
4
5
6
7
8
atomic() noexcept = default;
constexpr atomic (T val) noexcept;
atomic (const atomic&) = delete;

T operator= (T val) noexcept;
T operator= (T val) volatile noexcept;
atomic& operator= (const atomic&) = delete;
atomic& operator= (const atomic&) volatile = delete;
  1. 构造一个未初始化的原子对象。
  2. 构造一个用 val 初始化的原子对象。
  3. 禁用拷贝构造函数,原子对象不可复制、移动
  4. 可以赋值(val)。

memory_order

1
2
3
4
5
6
7
8
typedef enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst
} memory_order;

它们的目的是为了做线程间的同步,原理是在线程内限制变量操作的顺序:

  1. memory_order_relaxed:用于读写,不做任何限制。
  2. memory_order_acquire:用于读,如果一个原子变量的 load 用了该选项,那么可以保证,在本线程内,该 load 语句之后的所有变量(不论是否原子变量)的读写语句,都实际在该 load 操作执行后执行。
  3. memory_order_release:用于写,如果一个原子变量的 store 用了该选项,那么可以保证,在本线程内,该 store 语句之前的所有变量(不论是否原子变量)的读写语句,都实际在该 store 操作执行前执行。
  4. memory_order_consume:用于读,如果一个原子变量的 load 用了该选项,那么可以保证,在本线程内,该 load 语句之后的所有依赖该变量的变量的读写语句,都实际在该 load 操作执行后执行。但是,它只保证与当前操作相关的数据依赖关系。从 2016 年后,所有编译器实现中,memory_order_consume 和 memory_order_acquire 完全一致。
  5. memory_order_acq_rel:memory_order_acquire + memory_order_release
  6. memory_order_seq_cst:sequence consistent,顺序一致,要求所有变量的读写执行顺序都和代码中的顺序一致。

memory_order_acquire 和 memory_order_release 直观上就像一个栅栏:在调用处设置一个栅栏,acquire 是拦住代码中在它后边的变量读写操作,不让其跑到它前边;release则相反,不让前边的跑到后边。(cpu 提供 memory_barrier 或者 memory_fence 指令)关于为什么 cpu 会进行内存重排,请见内存模型与内存序

atomic::store()

1
2
void store (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
void store (T val, memory_order sync = memory_order_seq_cst) noexcept;

用 val 替换原子对象中的值。该操作是原子性的,通过 sync 指定内存顺序。sync 可选项见上文。

atomic::load()

1
2
T load (memory_order sync = memory_order_seq_cst) const volatile noexcept;
T load (memory_order sync = memory_order_seq_cst) const noexcept;

返回原子对象中的值。该操作为原子性。

atomic::exchange

1
2
T exchange (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T exchange (T val, memory_order sync = memory_order_seq_cst) noexcept;

用 val 替换原子对象中的值,并返回替换前的值。操作为原子性。整个过程完成之前,其他线程无法访问。

atomic::compare_exchange_weak

1
2
3
4
5
bool compare_exchange_weak (T &expected, T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
bool compare_exchange_weak (T &expected, T val, memory_order sync = memory_order_seq_cst) noexcept;

bool compare_exchange_weak (T &expected, T val, memory_order success, memory_order failure) volatile noexcept;
bool compare_exchange_weak (T &expected, T val, memory_order success, memory_order failure) noexcept;

将原子对象的存储值和预期值比较:

  • 若为 true,用 val 替换原子对象值。
  • 若为 false, 用包含的值替换预期值。

整个过程是原子性的。

下面版本使用的内存顺序取决于比较结果:true 则使用 success; false 则使用 failure。该函数比较的是原子对象和预期值中的物理内容,这可能导致使用操作符 == 比较相等的值的在这里比较失败。

与 compare_exchange_strong 不同,该 weak 版本允许错误的返回 false,即使原子对象存储值与预期值相等。对于某些循环算法,这可能是可接受的行为,并且可能在某些平台上显著提高性能。对于这些虚假的失败,函数返回 false,但不修改预期的值。对于非循环算法,通常首选 compare_exchange_strong。

atomic::compare_exchange_strong

1
2
3
4
5
bool compare_exchange_strong (T &expected, T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
bool compare_exchange_strong (T &expected, T val, memory_order sync = memory_order_seq_cst) noexcept;

bool compare_exchange_strong (T &expected, T val, memory_order success, memory_order failure) volatile noexcept;
bool compare_exchange_strong (T &expected, T val, memory_order success, memory_order failure) noexcept;

原子操作。与 compare_exchange_week 不同,当期望值与对象存储的值相等时,这个强版本必须始终返回 true,不允许虚假的失败。但是,在某些机器上,对于某些在循环中检查这个的算法,compare_exchange_weak 可能有更高的性能表现。

专门化计算操作

atomic::fetch_add(T val, memory_order sync = memory_order_seq_cst)

存储的值 + val 并返回之前的值,整个操作是原子性的。如果第二个参数使用默认值,那么这个函数相当于 atomic::operator+=。

atomic::fetch_sub(T val, memory_order sync = memory_order_seq_cst)

存储的值 - val 并返回之前的值,整个操作是原子性的。如果第二个参数使用默认值,那么这个函数相当于 atomic::operator-=。

atomic::fetch_and(T val, memory_order sync = memory_order_seq_cst)

(存储的值 & val)并返回之前的值,整个操作是原子性的。如果第二个参数使用默认值,那么这个函数相当于 atomic::operator&=。

atomic::fetch_or(T val, memory_order sync = memory_order_seq_cst)

(存储的值 | val)并返回之前的值,整个操作是原子性的。如果第二个参数使用默认值,那么这个函数相当于 atomic::operator|=。

atomic::fetch_xor(T val, memory_order sync = memory_order_seq_cst)

(存储的值 ^ val)并返回之前的值,整个操作是原子性的。如果第二个参数使用默认值,那么这个函数相当于 atomic::operator^=。

atomic::operator++ ()

递增保存的值,返回递增后的值。

atomic::operator++ (int)

递增保存的值,返回递增前的值。

atomic::operator-- ()

递减保存的值,返回递减后的值。

atomic::operator-- (int)

递减保存的值,返回递减前的值。

reference:https://blog.csdn.net/u014673282/article/details/89789139