5

现代C++ [5]: 原子操作

 11 months ago
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]: 原子操作

发表于2023-02-16|更新于2023-05-05|语言
阅读量:4

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?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK