前言

std::atomic模板的实例化或全特化可以定义一个原子的数据类型。它提供了多线程间的原子操作。所谓原子操作,一般是指在逻辑上的不可分割的操作(物理上并不一定只有一条指令)。原子操作是无锁类型,但不代表无需等待,当多个线程同时竞争同一原子变量时,仍要为了同步而等待。但原子操作的开销要低于加解锁。

原子操作(std::atomic)

原型

// since C++11
// Defined in header <atomic>
// 原始模板
template< class T >
struct atomic;

// 裸指针类型偏特化
template< class U >
struct atomic<U*>;

// since C++20
// Defined in header <memory>
// 智能指针类型的偏特化
template< class U >
struct atomic<std::shared_ptr<U>>;

template< class U >
struct atomic<std::weak_ptr<U>>;

对原子对象的访问可以建立线程间同步,并按照std::memory_order指定的顺序对非原子内存访问进行排序。

std::atomic既不可复制,也不可移动。

特化

原始模板

原始模板std::atomic可以被任何可平凡拷贝TriviallyCopyable)的类型T实例化,若T为class类型,则需要满足可拷贝构造CopyConstructible)和可拷贝赋值CopyAssignable)。

可拷贝构造需要满足可移动构造,即可移动构造是可拷贝构造的必要不充分条件;可拷贝赋值和可移动赋值同理。

所以,如果以下值为false,则T不可以用于实例化std::atomic

  • std::is_trivially_copyable<T>::value

  • std::is_copy_constructible<T>::value

  • std::is_move_constructible<T>::value

  • std::is_copy_assignable<T>::value

  • std::is_move_assignable<T>::value

偏特化

C++标准库为以下类型提供了std::atomic的偏特化,这些偏特化拥有原始模板没有的一些特性:

  • std::atomic<U*>偏特化:适用所有的指针类型。这些偏特化拥有标准布局(standard layout)、平凡的默认构造函数(trivial default constructors, (until C++20))和平凡的析构函数(trivial destructors)。除了为所有原子类型提供的操作之外,这些偏特化还支持适用于指针类型的原子算术操作,例如fetch_addfetch_sub
  • std::atomic<std::shared_ptr>>std::atomic<std::weak_ptr>>偏特化:适用于智能指针的偏特化。

针对整数类型的特化

当使用以下整数类型之一实例化时,std::atomic提供了适用于整数类型的其他原子操作,如 fetch_add, fetch_sub, fetch_and, fetch_or, fetch_xor

  • 字符类型:charchar8_t(since C++20),char16_tchar32_twchar_t
  • 标准有符号整型:signed charshortintlonglong long
  • 标准无符号整型:unsigned charunsigned shortunsigned intunsigned longunsigned long long
  • 在头文件<cstdint>中typedef的任何其他整数类型;

同样,这些特化拥有标准布局(standard layout)、平凡的默认构造函数(trivial default constructors, (until C++20))和平凡的析构函数(trivial destructors)。有符号整型的算术操作被定义为使用二的补码。

针对浮点数类型的特化(since C++20)

当使用标准浮点类型之一,如floatdoublelong double或扩展的浮点类型(since C++23),进行实例化时,std::atomic提供了适用于浮点数类型的其他原子操作,如 fetch_add, fetch_sub

类型别名

using atomic_bool = atomic<bool>;

其他类型别名自行查表

成员函数

函数名 解释
ctor 构造一个原子对象
operator= 将值存储到原子对象中
is_lock_free 检查原子对象是否无锁
store 用非原子参数原子地替换原子对象的值
load 原子地获得原子对象的值
operator T 从一个原子对象中加载值
exchange 原子地替换原子对象的值,并获得先前保存的值
compare_exchange_weak
compare_exchange_strong
将原子对象的值与非原子参数进行原子比较,如果相等则执行原子交换,如果不相等,则执行原子获取
wait(C++20) 阻塞线程,直到被通知(notified)并且原子值更改
notify_one(C++20) 通知至少一个等待原子对象的线程
notify_all(C++20) 通知所有因等待原子对象而被阻塞的线程

特化成员函数

函数名 解释
fetch_add 将参数原子地添加到存储在原子对象中的值,并获得先前保存的值
fetch_sub 从存储在原子对象中的值中原子地减去参数,并获得先前保存的值
fetch_and 原子地在参数和原子对象的值之间执行逐位“与”运算,并获得先前保存的值
fetch_or 原子地在参数和原子对象的值之间执行逐位“或”,并获得先前保存的值
fetch_xor 原子地在参数和原子对象的值之间执行逐位“异或”,并获得先前保存的值
operator++
operator++(int)
operator--
operator--(int)
将原子值增加或减少一
operator+=
operator-=
operator&=
operator|=
operator^=
将原子值与参数相加、相减或执行位与、位或、位异或

参考文献

[1] https://en.cppreference.com/w/cpp/atomic/atomic

[2] https://en.cppreference.com/w/cpp/language/memory_model

[3] https://en.cppreference.com/w/cpp/atomic/memory_order

[4] C++:Trivial、Standard-Layout 和 POD

[5] 如何理解 C++11 的六种 memory order?