
关注公众号获取更多信息:

C++11提供了一个原子类型std::atomic<T>,可以使用任意类型作为模板参数,C++11内置了整型的原子变量,可以方便的使用原子变量,使用原子变量就不用互斥变量来保护该变量了。
每个 std::atomic 模板的实例化和全特化定义一个原子类型。若一个线程写入原子对象,同时另一线程从它读取,则行为良好定义(数据竞争的细节我们会在下节--内存模型里讲解)
另外,对原子对象的访问可以建立线程间同步,并按 std::memory_order 所对非原子内存访问定序。
需要注意的是,std::atomic 既不可复制亦不可移动。
由于std::atomic 是一个模板,C++11中定义了该模板特化的一些要求:
主模板
主 std::atomic 模板可用任何满足可复制构造 (CopyConstructible) 及可复制赋值 (CopyAssignable) 的可平凡复制 (TriviallyCopyable) 类型 T 特化。若下列任何值为 false 则程序为谬构:
-
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
std::atomic<bool> 使用初等模板。它保证是标准布局结构体。
偏特化
标准库为下列类型提供 std::atomic 模板的特化,它们拥有初等模板所不拥有的额外属性:
对所有指针类型的部分特化 std::atomic<U*> 。这些特化拥有标准布局、平凡默认构造函数 (C++20 前)和平凡析构函数。除了为所有原子类型提供的操作,这些特化额外支持适合指针类型的原子算术运算,例如 fetch_add 、 fetch_sub 。
对整数类型的特化
以下列整数类型之一实例化时, std::atomic 提供适合于整数类型的额外原子操作,例如 fetch_add 、 fetch_sub 、 fetch_and 、 fetch_or 、 fetch_xor :
-
-
字符类型 char 、 char8_t (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 所需的额外整数类型。
-
另外,结果的 std::atomic<Integral> 特化拥有标准布局、平凡默认构造函数 (C++20 前)和平凡析构函数。定义有符号整数算术为使用补码;无未定义结果。
atomic 用法
这次我们就不一一介绍atomic的API了,我们从atomic的定义反观atomic的用法:
template < class T > struct atomic {bool is_lock_free() const volatile;bool is_lock_free() const;void store(T, memory_order = memory_order_seq_cst) volatile;void store(T, memory_order = memory_order_seq_cst);T load(memory_order = memory_order_seq_cst) const volatile;T load(memory_order = memory_order_seq_cst) const;operator T() const volatile;operator T() const;T exchange(T, memory_order = memory_order_seq_cst) volatile;T exchange(T, memory_order = memory_order_seq_cst);bool compare_exchange_weak(T &, T, memory_order, memory_order) volatile;bool compare_exchange_weak(T &, T, memory_order, memory_order);bool compare_exchange_strong(T &, T, memory_order, memory_order) volatile;bool compare_exchange_strong(T &, T, memory_order, memory_order);bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst) volatile;bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst);bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst) volatile;bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst);atomic() = default;constexpr atomic(T);atomic(const atomic &) = delete;atomic & operator=(const atomic &) = delete;atomic & operator=(const atomic &) volatile = delete;T operator=(T) volatile;T operator=(T);};
可以看出,我们常用的API如下:
|
(构造函数) | 构造原子对象 (公开成员函数) |
|
operator= | 存储值于原子对象 (公开成员函数) |
|
is_lock_free | 检查原子对象是否免锁 (公开成员函数) |
|
store | 原子地以非原子对象替换原子对象的值 (公开成员函数) |
|
load | 原子地获得原子对象的值 (公开成员函数) |
|
operator T | 从原子对象加载值 (公开成员函数) |
|
exchange | 原子地替换原子对象的值并获得它先前持有的值 (公开成员函数) |
|
compare_exchange_weakcompare_exchange_strong | 原子地比较原子对象与非原子参数的值,若相等则进行交换,若不相等则进行加载 (公开成员函数) |
除了这些常用的API,我们常用的API还有一些特化的API(之前我们说过,只有整数类型和指针类型有特化,所以这些API只有整数类型和指针类型可用):
|
fetch_add | 原子地将参数加到存储于原子对象的值,并返回先前保有的值 (公开成员函数) |
|
fetch_sub | 原子地从存储于原子对象的值减去参数,并获得先前保有的值 (公开成员函数) |
|
fetch_and | 原子地进行参数和原子对象的值的逐位与,并获得先前保有的值 (公开成员函数) |
|
fetch_or | 原子地进行参数和原子对象的值的逐位或,并获得先前保有的值 (公开成员函数) |
|
fetch_xor | 原子地进行参数和原子对象的值的逐位异或,并获得先前保有的值 (公开成员函数) |
|
operator++operator++(int)operator--operator--(int) | 令原子值增加或减少一 (公开成员函数) |
|
operator+=operator-=operator&=operator|=operator^= | 加、减,或与原子值进行逐位与、或、异或 (公开成员函数) |
例子
下面我们用一个例子结束本文:
// constructing atomics#include <iostream> // std::cout#include <atomic> // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT#include <thread> // std::thread, std::this_thread::yield#include <vector> // std::vectorstd::atomic<bool> ready (false);std::atomic_flag winner = ATOMIC_FLAG_INIT;void count1m (int id) {while (!ready) { std::this_thread::yield(); } // wait for the ready signalfor (volatile int i=0; i<1000000; ++i) {} // go!, count to 1 millionif (!winner.test_and_set()) { std::cout << "thread #" << id << " won!\n"; }};int main (){std::vector<std::thread> threads;std::cout << "spawning 10 threads that count to 1 million...\n";for (int i=1; i<=10; ++i) threads.push_back(std::thread(count1m,i));ready = true;for (auto& th : threads) th.join();return 0;}
可能的输出:
spawning 10 threads that count to 1 million...thread #7 won!
本文详细介绍了C++11中std::atomic<T>原子类型,包括其模板特化、原子操作原理、使用场景和常见API。重点展示了如何使用原子变量避免数据竞争,以及如何通过std::atomic实现线程同步。通过实例演示了原子操作在并发编程中的应用。
1574

被折叠的 条评论
为什么被折叠?



