现代C++ [5]: 原子操作
source link: https://no5-aaron-wu.github.io/2023/02/16/cpp-modern-5-Atomic/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
现代C++ [5]: 原子操作
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_add
、fetch_sub
。std::atomic<std::shared_ptr>>
和std::atomic<std::weak_ptr>>
偏特化:适用于智能指针的偏特化。
针对整数类型的特化
当使用以下整数类型之一实例化时,std::atomic
提供了适用于整数类型的其他原子操作,如 fetch_add
, fetch_sub
, fetch_and
, fetch_or
, fetch_xor
:
- 字符类型:
char
,char8_t
(since C++20),char16_t
,char32_t
和wchar_t
; - 标准有符号整型:
signed char
,short
,int
,long
和long long
; - 标准无符号整型:
unsigned char
,unsigned short
,unsigned int
,unsigned long
和unsigned long long
; - 在头文件
<cstdint>
中typedef的任何其他整数类型;
同样,这些特化拥有标准布局(standard layout)、平凡的默认构造函数(trivial default constructors, (until C++20))和平凡的析构函数(trivial destructors)。有符号整型的算术操作被定义为使用二的补码。
针对浮点数类型的特化(since C++20)
当使用标准浮点类型之一,如float
,double
,long 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
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK