所谓的原子操作,取的就是“原子是最小的、不可分割的最小个体”的意义,它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。也就是他确保了在同一时刻只有唯一的线程对这个资源进行访问。这有点类似互斥对象对共享资源的访问的保护,但是原子操作更加接近底层,因而效率更高。
在以往的C++标准中并没有对原子操作进行规定,我们往往是使用汇编语言,或者是借助第三方的线程库,例如intel的pthread来实现。在新标准C++11,引入了原子操作的概念,并通过这个新的头文件提供了多种原子操作数据类型,例如,atomic_bool,atomic_int等等,如果我们在多个线程中对这些类型的共享资源进行操作,编译器将保证这些操作都是原子性的,也就是说,确保任意时刻只有一个线程对这个资源进行访问,编译器将保证,多个线程访问这个共享资源的正确性。从而避免了锁的使用,提高了效率。
我们还是来看一个实际的例子。假若我们要设计一个广告点击统计程序,在服务器程序中,使用多个线程模拟多个用户对广告的点击:
#include <boost/thread/thread.hpp>
#include <atomic>
#include <iostream>
#include <time.h>
using namespace std;
// 全局的结果数据
long total = 0;
// 点击函数
void click()
{
for(int i=0; i<1000000;++i)
{
// 对全局数据进行无锁访问
total += 1;
}
}
int main(int argc, char* argv[])
{
// 计时开始
clock_t start = clock();
// 创建100个线程模拟点击统计
boost::thread_group threads;
for(int i=0; i<100;++i)
{
threads.create_thread(click);
}
threads.join_all();
// 计时结束
clock_t finish = clock();
// 输出结果
cout<<"result:"<<total<<endl;
cout<<"duration:"<<finish -start<<"ms"<<endl;
return 0;
}
从执行的结果来看,这样的方法虽然非常快,但是结果不正确
E:SourceCodeMinGW>thread.exe
result:87228026
duration:528ms
很自然地,我们会想到使用互斥对象来对全局共享资源的访问进行保护,于是有了下面的实现:
long total = 0;
// 对共享资源进行保护的互斥对象
mutex m;
void click()
{
for(int i=0; i<1000000;++i)
{
// 访问之前,锁定互斥对象
m.lock();
total += 1;
// 访问完成后,释放互斥对象
m.unlock();
}
}
互斥对象的使用,保证了同一时刻只有唯一的一个线程对这个共享进行访问,从执行的结果来看,互斥对象保证了结果的正确性,但是也有非常大的性能损失,从刚才的528ms变成了现在的8431,用了原来时间的10多倍的时间。这个损失够大。
E:SourceCodeMinGW>thread.exe
result:100000000
duration:8431ms
如果是在C++11之前,我们的解决方案也就到此为止了,但是,C++对性能的追求是永无止境的,他总是想尽一切办法榨干CPU的性能。在C++11中,实现了原子操作的数据类型(atomic_bool,atomic_int,atomic_long等等),对于这些原子数据类型的共享资源的访问,无需借助mutex等锁机制,也能够实现对共享资源的正确访问。
// 引入原子数据类型的头文件
#include <atomic>
// 用原子数据类型作为共享资源的数据类型
atomic_long total(0);
//long total = 0;
void click()
{
for(int i=0; i<1000000;++i)
{
// 仅仅是数据类型的不同而以,对其的访问形式与普通数据类型的资源并无区别
total += 1;
}
}
我们来看看使用原子数据类型之后的效果如何:
E:SourceCodeMinGW>thread.exe
result:100000000
duration:2105ms
结果正确!耗时只是使用mutex互斥对象的四分之一!也仅仅是不采用任何保护机制的时间的4倍。可以说这是一个非常不错的成绩了。
原子操作的实现跟普通数据类型类似,但是它能够在保证结果正确的前提下,提供比mutex等锁机制更好的性能,如果我们要访问的共享资源可以用原子数据类型表示,那么在多线程程序中使用这种新的等价数据类型,是一个不错的选择。
一
C++11 并发指南已经写了 5 章,前五章重点介绍了多线程编程方面的内容,但大部分内容只涉及多线程、互斥量、条件变量和异步编程相关的 API,C++11 程序员完全可以不必知道这些 API 在底层是如何实现的,只需要清楚 C++11 多线程和异步编程相关 API 的语义,然后熟加练习即可应付大部分多线程编码需求。但是在很多极端的场合下为了性能和效率,我们需要开发一些 lock-free 的算法和数据结构,前面几章的内容可能就派不上用场了,因此从本文开始介绍 C++11 标准中 <atomic> 头文件里面的类和相关函数。
本文介绍 <atomic> 头文件中最简单的原子类型: atomic_flag。atomic_flag 一种简单的原子布尔类型,只支持两种操作,test-and-set 和 clear。
std::atomic_flag 构造函数
std::atomic_flag 构造函数如下:
- atomic_flag() noexcept = default;
- atomic_flag (const atomic_flag&T) = delete;
std::atomic_flag 只有默认构造函数,拷贝构造函数已被禁用,因此不能从其他的 std::atomic_flag 对象构造一个新的 std::atomic_flag 对象。
如果在初始化时没有明确使用 ATOMIC_FLAG_INIT初始化,那么新创建的 std::atomic_flag 对象的状态是未指定的(unspecified)(既没有被 set 也没有被 clear。)另外,atomic_flag不能被拷贝,也不能 move 赋值。
ATOMIC_FLAG_INIT: 如果某个 std::atomic_flag 对象使用该宏初始化,那么可以保证该 std::atomic_flag 对象在创建时处于 clear 状态。
下面先看一个简单的例子,main() 函数中创建了 10 个线程进行计数,率先完成计数任务的线程输出自己的 ID,后续完成计数任务的线程不会输出自身 ID:
#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::vector std::atomic<bool> ready(false); // can be checked without being set std::atomic_flag winner = ATOMIC_FLAG_INIT; // always set when checked void count1m(int id) { while (!ready) { std::this_thread::yield(); } // 等待主线程中设置 ready 为 true. for (int i = 0; i < 1000000; ++i) { } // 计数. // 如果某个线程率先执行完上面的计数过程,则输出自己的 ID. // 此后其他线程执行 test_and_set 是 if 语句判断为 false, // 因此不会输出自身 ID. if (!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; }
多次执行结果如下:
atomic ) ./Atomic-Flag1 spawning 10 threads that count to 1 million... thread #6 won! atomic ) ./Atomic-Flag1 spawning 10 threads that count to 1 million... thread #1 won! atomic ) ./Atomic-Flag1 spawning 10 threads that count to 1 million... thread #5 won! atomic ) ./Atomic-Flag1 spawning 10 threads that count to 1 million... thread #1 won! atomic ) ./Atomic-Flag1 spawning 10 threads that count to 1 million... thread #1 won! atomic ) ./Atomic-Flag1 spawning 10 threads that count to 1 million... thread #10 won!
std::atomic_flag::test_and_set 介绍
std::atomic_flag 的 test_and_set 函数原型如下:
bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept; bool test_and_set (memory_order sync = memory_order_seq_cst) noexcept;
test_and_set() 函数检查 std::atomic_flag 标志,如果 std::atomic_flag 之前没有被设置过,则设置 std::atomic_flag 的标志,并返回先前该 std::atomic_flag 对象是否被设置过,如果之前 std::atomic_flag 对象已被设置,则返回 true,否则返回 false。
test-and-set 操作是原子的(因此 test-and-set 是原子 read-modify-write (RMW)操作)。
test_and_set 可以指定 Memory Order(后续的文章会详细介绍 C++11 的 Memory Order,此处为了完整性列出 test_and_set 参数 sync 的取值),取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
一个简单的例子:
#include <iostream> // std::cout #include <atomic> // std::atomic_flag #include <thread> // std::thread #include <vector> // std::vector #include <sstream> // std::stringstream std::atomic_flag lock_stream = ATOMIC_FLAG_INIT; std::stringstream stream; void append_number(int x) { while (lock_stream.test_and_set()) { } stream << "thread #" << x << '\n'; lock_stream.clear(); } int main() { std::vector < std::thread > threads; for (int i = 1; i <= 10; ++i) threads.push_back(std::thread(append_number, i)); for (auto & th:threads) th.join(); std::cout << stream.str() << std::endl;; return 0; }
执行结果如下:
thread #1 thread #2 thread #3 thread #4 thread #5 thread #6 thread #7 thread #8 thread #9 thread #10
std::atomic_flag::clear() 介绍
清除 std::atomic_flag 对象的标志位,即设置 atomic_flag 的值为 false。clear 函数原型如下:
void clear (memory_order sync = memory_order_seq_cst) volatile noexcept; void clear (memory_order sync = memory_order_seq_cst) noexcept;
清除 std::atomic_flag 标志使得下一次调用 std::atomic_flag::test_and_set 返回 false。
std::atomic_flag::clear() 可以指定 Memory Order(后续的文章会详细介绍 C++11 的 Memory Order,此处为了完整性列出 clear 参数 sync 的取值),取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
结合 std::atomic_flag::test_and_set() 和 std::atomic_flag::clear(),std::atomic_flag 对象可以当作一个简单的自旋锁使用,请看下例:
#include <thread> #include <vector> #include <iostream> #include <atomic> std::atomic_flag lock = ATOMIC_FLAG_INIT; void f(int n) { for (int cnt = 0; cnt < 100; ++cnt) { while (lock.test_and_set(std::memory_order_acquire)) // acquire lock ; // spin std::cout << "Output from thread " << n << '\n'; lock.clear(std::memory_order_release); // release lock } } int main() { std::vector<std::thread> v; for (int n = 0; n < 10; ++n) { v.emplace_back(f, n); } for (auto& t : v) { t.join(); } }
在上面的程序中,std::atomic_flag 对象 lock 的上锁操作可以理解为 lock.test_and_set(std::memory_order_acquire); (此处指定了 Memory Order,更多有关 Memory Order 的概念,我会在后续的文章中介绍),解锁操作相当与 lock.clear(std::memory_order_release)。
在上锁的时候,如果 lock.test_and_set 返回 false,则表示上锁成功(此时 while 不会进入自旋状态),因为此前 lock 的标志位为 false(即没有线程对 lock 进行上锁操作),但调用 test_and_set 后 lock 的标志位为 true,说明某一线程已经成功获得了 lock 锁。
如果在该线程解锁(即调用 lock.clear(std::memory_order_release)) 之前,另外一个线程也调用 lock.test_and_set(std::memory_order_acquire) 试图获得锁,则 test_and_set(std::memory_order_acquire) 返回 true,则 while 进入自旋状态。如果获得锁的线程解锁(即调用了 lock.clear(std::memory_order_release))之后,某个线程试图调用 lock.test_and_set(std::memory_order_acquire) 并且返回 false,则 while 不会进入自旋,此时表明该线程成功地获得了锁。
按照上面的分析,我们知道在某种情况下 std::atomic_flag 对象可以当作一个简单的自旋锁使用。
二
C++11 中最简单的原子类型 std::atomic_flag,但是 std::atomic_flag 过于简单,只提供了 test_and_set 和 clear 两个 API,不能满足其他需求(如 store, load, exchange, compare_exchange 等),因此本文将介绍功能更加完善的 std::atomic 类。
std::atomic 基本介绍
std::atomic 是模板类,一个模板类型为 T 的原子对象中封装了一个类型为 T 的值。
template <class T> struct atomic;
原子类型对象的主要特点就是从不同线程访问不会导致数据竞争(data race)。因此从不同线程访问某个原子对象是良性 (well-defined) 行为,而通常对于非原子类型而言,并发访问某个对象(如果不做任何同步操作)会导致未定义 (undifined) 行为发生。
C++11 标准中的基本 std::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); };
另外,C++11 标准库 std::atomic 提供了针对整形(integral)和指针类型的特化实现,分别定义如下:
针对整形(integal)的特化,其中 integal 代表了如下类型char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, char16_t, char32_t, wchar_t:
template
<>
struct
atomic<integral> {
bool
is_lock_free()
const
volatile
;
bool
is_lock_free()
const
;
void
store(integral, memory_order = memory_order_seq_cst)
volatile
;
void
store(integral, memory_order = memory_order_seq_cst);
integral load(memory_order = memory_order_seq_cst)
const
volatile
;
integral load(memory_order = memory_order_seq_cst)
const
;
operator integral()
const
volatile
;
operator integral()
const
;
integral exchange(integral, memory_order = memory_order_seq_cst)
volatile
;
integral exchange(integral, memory_order = memory_order_seq_cst);
bool
compare_exchange_weak(integral&, integral, memory_order, memory_order)
volatile
;
bool
compare_exchange_weak(integral&, integral, memory_order, memory_order);
bool
compare_exchange_strong(integral&, integral, memory_order, memory_order)
volatile
;
bool
compare_exchange_strong(integral&, integral, memory_order, memory_order);
bool
compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst)
volatile
;
bool
compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst);
bool
compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst)
volatile
;
bool
compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst);
integral fetch_add(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_add(integral, memory_order = memory_order_seq_cst);
integral fetch_sub(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_sub(integral, memory_order = memory_order_seq_cst);
integral fetch_and(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_and(integral, memory_order = memory_order_seq_cst);
integral fetch_or(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_or(integral, memory_order = memory_order_seq_cst);
integral fetch_xor(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_xor(integral, memory_order = memory_order_seq_cst);
atomic() =
default
;
constexpr
atomic(integral);
atomic(
const
atomic&) =
delete
;
atomic& operator=(
const
atomic&) =
delete
;
atomic& operator=(
const
atomic&)
volatile
=
delete
;
integral operator=(integral)
volatile
;
integral operator=(integral);
integral operator++(
int
)
volatile
;
integral operator++(
int
);
integral operator--(
int
)
volatile
;
integral operator--(
int
);
integral operator++()
volatile
;
integral operator++();
integral operator--()
volatile
;
integral operator--();
integral operator+=(integral)
volatile
;
integral operator+=(integral);
integral operator-=(integral)
volatile
;
integral operator-=(integral);
integral operator&=(integral)
volatile
;
integral operator&=(integral);
integral operator|=(integral)
volatile
;
integral operator|=(integral);
integral operator^=(integral)
volatile
;
integral operator^=(integral);
};
|
针对指针的特化:
template
<
class
T>
struct
atomic<T*> {
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);
T* fetch_add(
ptrdiff_t
, memory_order = memory_order_seq_cst)
volatile
;
T* fetch_add(
ptrdiff_t
, memory_order = memory_order_seq_cst);
T* fetch_sub(
ptrdiff_t
, memory_order = memory_order_seq_cst)
volatile
;
T* fetch_sub(
ptrdiff_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*);
T* operator++(
int
)
volatile
;
T* operator++(
int
);
T* operator--(
int
)
volatile
;
T* operator--(
int
);
T* operator++()
volatile
;
T* operator++();
T* operator--()
volatile
;
T* operator--();
T* operator+=(
ptrdiff_t
)
volatile
;
T* operator+=(
ptrdiff_t
);
T* operator-=(
ptrdiff_t
)
volatile
;
T* operator-=(
ptrdiff_t
);
};
|
std::atomic 成员函数
好了,对 std::atomic 有了一个最基本认识之后我们来看 std::atomic 的成员函数吧。
std::atomic 构造函数
std::atomic 的构造函数如下:
default (1) | atomic() noexcept = default; |
---|---|
initialization (2) | constexpr atomic (T val) noexcept; |
copy [deleted] (3) | atomic (const atomic&) = delete; |
- 默认构造函数,由默认构造函数创建的 std::atomic 对象处于未初始化(uninitialized)状态,对处于未初始化(uninitialized)状态 std::atomic对象可以由 atomic_init 函数进行初始化。
- 初始化构造函数,由类型 T初始化一个 std::atomic对象。
- 拷贝构造函数被禁用。
请看下例:
#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::vector
// 由 false 初始化一个 std::atomic<bool> 类型的原子变量
std::atomic<
bool
> ready(
false
);
std::atomic_flag winner = ATOMIC_FLAG_INIT;
void
do_count1m(
int
id)
{
while
(!ready) { std::this_thread::yield(); }
// 等待 ready 变为 true.
for
(
volatile
int
i=0; i<1000000; ++i) {}
// 计数
if
(!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;
}
|
std::atomic::operator=() 函数
std::atomic 的赋值操作函数定义如下:
set value (1) | T operator= (T val) noexcept; T operator= (T val) volatile noexcept; |
---|---|
copy [deleted] (2) | atomic& operator= (const atomic&) = delete; atomic& operator= (const atomic&) volatile = delete; |
可以看出,普通的赋值拷贝操作已经被禁用。但是一个类型为 T 的变量可以赋值给相应的原子类型变量(相当与隐式转换),该操作是原子的,内存序(Memory Order) 默认为顺序一致性(std::memory_order_seq_cst),如果需要指定其他的内存序,需使用 std::atomic::store()。
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread, std::this_thread::yield
std::atomic <
int
> foo = 0;
void
set_foo(
int
x)
{
foo = x;
// 调用 std::atomic::operator=().
}
void
print_foo()
{
while
(foo == 0) {
// wait while foo == 0
std::this_thread::yield();
}
std::cout <<
"foo: "
<< foo <<
'\n'
;
}
int
main()
{
std::
thread
first(print_foo);
std::
thread
second(set_foo, 10);
first.join();
second.join();
return
0;
}
|
基本 std::atomic 类型操作
本节主要介绍基本 std::atomic 类型所具备的操作(即成员函数)。我们知道 std::atomic 是模板类,一个模板类型为 T 的原子对象中封装了一个类型为 T 的值。本文<std::atomic 基本介绍>一节中也提到了 std::atomic 类模板除了基本类型以外,还针对整形和指针类型做了特化。 特化的 std::atomic 类型支持更多的操作,如 fetch_add, fetch_sub, fetch_and 等。本小节介绍基本 std::atomic 类型所具备的操作:
-
is_lock_free
bool
is_lock_free()
const
volatile
noexcept
;
bool
is_lock_free()
const
noexcept
;
|
-
判断该 std::atomic 对象是否具备 lock-free 的特性。如果某个对象满足
lock-free
特性,在多个线程访问该对象时不会导致线程阻塞。(可能使用某种事务内存
transactional memory 方法实现 lock-free 的特性)。
-
store
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
;
|
-
修改被封装的值,std::atomic::store 函数将类型为 T 的参数 val 复制给原子对象所封装的值。T 是 std::atomic 类模板参数。另外参数 sync 指定内存序(Memory Order),可能的取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_release | Release |
memory_order_seq_cst | Sequentially consistent |
-
请看下面例子:
#include <iostream> // std::cout
#include <atomic> // std::atomic, std::memory_order_relaxed
#include <thread> // std::thread
std::atomic<
int
> foo(0);
// 全局的原子对象 foo
void
set_foo(
int
x)
{
foo.store(x, std::memory_order_relaxed);
// 设置(store) 原子对象 foo 的值
}
void
print_foo()
{
int
x;
do
{
x = foo.load(std::memory_order_relaxed);
// 读取(load) 原子对象 foo 的值
}
while
(x == 0);
std::cout <<
"foo: "
<< x <<
'\n'
;
}
int
main ()
{
std::
thread
first(print_foo);
// 线程 first 打印 foo 的值
std::
thread
second(set_foo, 10);
// 线程 second 设置 foo 的值
first.join();
second.join();
return
0;
}
|
-
load
T load (memory_order sync = memory_order_seq_cst)
const
volatile
noexcept
;
T load (memory_order sync = memory_order_seq_cst)
const
noexcept
;
|
-
读取被封装的值,参数 sync 设置内存序(Memory Order),可能的取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_seq_cst | Sequentially consistent |
-
请看下面例子:
#include <iostream> // std::cout
#include <atomic> // std::atomic, std::memory_order_relaxed
#include <thread> // std::thread
std::atomic<
int
> foo(0);
// 全局的原子对象 foo
void
set_foo(
int
x)
{
foo.store(x, std::memory_order_relaxed);
// 设置(store) 原子对象 foo 的值
}
void
print_foo()
{
int
x;
do
{
x = foo.load(std::memory_order_relaxed);
// 读取(load) 原子对象 foo 的值
}
while
(x == 0);
std::cout <<
"foo: "
<< x <<
'\n'
;
}
int
main ()
{
std::
thread
first(print_foo);
// 线程 first 打印 foo 的值
std::
thread
second(set_foo, 10);
// 线程 second 设置 foo 的值
first.join();
second.join();
return
0;
}
|
-
operator T
operator T()
const
volatile
noexcept
;
operator T()
const
noexcept
;
|
-
与 load 功能类似,也是
读取被封装的值,operator T() 是类型转换(
type-cast)
操作,默认的内存序是 std::memory_order_seq_cst,如果需要指定其他的内存序,你应该使用 load() 函数。请看下面例子:
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread, std::this_thread::yield
std::atomic<
int
> foo = 0;
std::atomic<
int
> bar = 0;
void
set_foo(
int
x)
{
foo = x;
}
void
copy_foo_to_bar()
{
// 如果 foo == 0,则该线程 yield,
// 在 foo == 0 时, 实际也是隐含了类型转换操作,
// 因此也包含了 operator T() const 的调用.
while
(foo == 0) std::this_thread::yield();
// 实际调用了 operator T() const, 将foo 强制转换成 int 类型,
// 然后调用 operator=().
bar =
static_cast
<
int
>(foo);
}
void
print_bar()
{
// 如果 bar == 0,则该线程 yield,
// 在 bar == 0 时, 实际也是隐含了类型转换操作,
// 因此也包含了 operator T() const 的调用.
while
(bar == 0) std::this_thread::yield();
std::cout <<
"bar: "
<< bar <<
'\n'
;
}
int
main ()
{
std::
thread
first(print_bar);
std::
thread
second(set_foo, 10);
std::
thread
third(copy_foo_to_bar);
first.join();
second.join();
third.join();
return
0;
}
|
-
exchange
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;
-
读取并修改被封装的值,exchange 会将 val 指定的值替换掉之前该原子对象封装的值,并返回之前该原子对象封装的值,整个过程是原子的(因此exchange 操作也称为
read-modify-write
操作)。sync参数指定内存序(Memory Order),可能的取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
请看下面例子,各个线程计数至 1M,首先完成计数任务的线程打印自己的 ID,
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
std::atomic<
bool
> ready(
false
);
std::atomic<
bool
> winner(
false
);
void
count1m (
int
id)
{
while
(!ready) {}
// wait for the ready signal
for
(
int
i = 0; i < 1000000; ++i) {}
// go!, count to 1 million
if
(!winner.exchange(
true
)) { 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;
}
|
-
compare_exchange_weak
(1) | 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; |
---|---|
(2) | 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; |
-
比较并交换被封装的值(weak)与参数
expected
所指定的值是否相等,如果:
- 相等,则用 val 替换原子对象的旧值。
- 不相等,则用原子对象的旧值替换 expected ,因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。
-
该函数通常会读取原子对象封装的值,如果比较为 true(即原子对象的值等于
expected),则替换原子对象的旧值,但整个操作是原子的,在某个线程读取和修改该原子对象时,另外的线程不能对读取和修改该原子对象。
在第 (2)种情况下,内存序(Memory Order)的选择取决于比较操作结果,如果比较结果为 true(即原子对象的值等于 expected),则选择参数 success 指定的内存序,否则选择参数 failure 所指定的内存序。
注意,该函数直接比较原子对象所封装的值与参数 expected 的物理内容,所以某些情况下,对象的比较操作在使用 operator==() 判断时相等,但 compare_exchange_weak 判断时却可能失败,因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3,它们在逻辑上都表示"真",但在物理上两者的表示并不相同)。
与 compare_exchange_strong 不同, weak 版本的 compare-and-exchange 操作允许( spuriously 地)返回 false(即原子对象所封装的值与参数 expected 的物理内容相同,但却仍然返回 false),不过在某些需要循环操作的算法下这是可以接受的,并且在一些平台下 compare_exchange_weak 的性能更好 。如果 compare_exchange_weak 的判断确实发生了伪失败( spurious failures)——即使原子对象所封装的值与参数 expected 的物理内容相同,但判断操作的结果却为 false,compare_exchange_weak函数返回 false,并且参数
expected 的值不会改变。
对于某些不需要采用循环操作的算法而言, 通常采用 compare_exchange_strong 更好。另外,该函数的内存序由 sync 参数指定,可选条件如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
请看下面的例子(
参考):
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
// a simple global linked list:
struct
Node {
int
value; Node* next; };
std::atomic<Node*> list_head(
nullptr
);
void
append(
int
val)
{
// append an element to the list
Node* newNode =
new
Node{val, list_head};
// next is the same as: list_head = newNode, but in a thread-safe way:
while
(!list_head.compare_exchange_weak(newNode->next,newNode)) {}
// (with newNode->next updated accordingly if some other thread just appended another node)
}
int
main ()
{
// spawn 10 threads to fill the linked list:
std::vector<std::
thread
> threads;
for
(
int
i = 0; i < 10; ++i) threads.push_back(std::
thread
(append, i));
for
(
auto
& th : threads) th.join();
// print contents:
for
(Node* it = list_head; it!=
nullptr
; it=it->next)
std::cout <<
' '
<< it->value;
std::cout <<
'\n'
;
// cleanup:
Node* it;
while
(it=list_head) {list_head=it->next;
delete
it;}
return
0;
}
|
-
可能的执行结果如下:
9 8 7 6 5 4 3 2 1 0
|
-
compare_exchange_strong
(1) | 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; |
---|---|
(2) | 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; |
-
比较并交换被封装的值(strong)与参数
expected
所指定的值是否相等,如果:
- 相等,则用 val 替换原子对象的旧值。
- 不相等,则用原子对象的旧值替换 expected ,因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。
-
该函数通常会读取原子对象封装的值,如果比较为 true(即原子对象的值等于
expected),则替换原子对象的旧值,但整个操作是原子的,在某个线程读取和修改该原子对象时,另外的线程不能对读取和修改该原子对象。
在第 (2)种情况下,内存序(Memory Order)的选择取决于比较操作结果,如果比较结果为 true(即原子对象的值等于 expected),则选择参数 success 指定的内存序,否则选择参数 failure 所指定的内存序。
注意,该函数直接比较原子对象所封装的值与参数 expected 的物理内容,所以某些情况下,对象的比较操作在使用 operator==() 判断时相等,但 compare_exchange_weak 判断时却可能失败,因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3,它们在逻辑上都表示"真",但在物理上两者的表示并不相同)。
-
与
compare_exchange_weak 不同, strong版本的 compare-and-exchange 操作不允许(
spuriously 地)返回 false,即原子对象所封装的值与参数
expected
的物理内容相同,比较操作一定会为 true。不过在某些平台下,如果算法本身需要循环操作来做检查, compare_exchange_weak 的性能会更好。
因此对于某些不需要采用循环操作的算法而言, 通常采用 compare_exchange_strong 更好。另外,该函数的内存序由 sync 参数指定,可选条件如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
请看下面的例子:
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
// a simple global linked list:
struct
Node {
int
value; Node* next; };
std::atomic<Node*> list_head(
nullptr
);
void
append(
int
val)
{
// append an element to the list
Node* newNode =
new
Node{val, list_head};
// next is the same as: list_head = newNode, but in a thread-safe way:
while
(!(list_head.compare_exchange_strong(newNode->next, newNode)));
// (with newNode->next updated accordingly if some other thread just appended another node)
}
int
main ()
{
// spawn 10 threads to fill the linked list:
std::vector<std::
thread
> threads;
for
(
int
i = 0; i < 10; ++i) threads.push_back(std::
thread
(append, i));
for
(
auto
& th : threads) th.join();
// print contents:
for
(Node* it = list_head; it!=
nullptr
; it=it->next)
std::cout <<
' '
<< it->value;
std::cout <<
'\n'
;
// cleanup:
Node* it;
while
(it=list_head) {list_head=it->next;
delete
it;}
return
0;
}
|
好了,本文花了大量的篇幅介绍 std::atomic 基本类型,下一篇博客我会给大家介绍 C++11 的标准库中std::atomic 针对整形(integral)和指针类型的特化版本做了哪些改进。
三
基本的原子类型 std::atomic 的用法,本节我会给大家介绍C++11 标准库中的 std::atomic 针对整形(integral)和指针类型的特化版本做了哪些改进。
总地来说,C++11 标准库中的 std::atomic 针对整形(integral)和指针类型的特化版本新增了一些算术运算和逻辑运算操作。具体如下:
integral fetch_add(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_add(integral, memory_order = memory_order_seq_cst);
integral fetch_sub(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_sub(integral, memory_order = memory_order_seq_cst);
integral fetch_and(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_and(integral, memory_order = memory_order_seq_cst);
integral fetch_or(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_or(integral, memory_order = memory_order_seq_cst);
integral fetch_xor(integral, memory_order = memory_order_seq_cst)
volatile
;
integral fetch_xor(integral, memory_order = memory_order_seq_cst);
integral operator++(
int
)
volatile
;
integral operator++(
int
);
integral operator--(
int
)
volatile
;
integral operator--(
int
);
integral operator++()
volatile
;
integral operator++();
integral operator--()
volatile
;
integral operator--();
integral operator+=(integral)
volatile
;
integral operator+=(integral);
integral operator-=(integral)
volatile
;
integral operator-=(integral);
integral operator&=(integral)
volatile
;
integral operator&=(integral);
integral operator|=(integral)
volatile
;
integral operator|=(integral);
integral operator^=(integral)
volatile
;
integral operator^=(integral);
|
下面我们来简单介绍以上的 std::atomic 特化版本的成员函数。
-
fetch_add
if T is integral (1) | T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; T fetch_add (T val, memory_order sync = memory_order_seq_cst) noexcept; |
---|---|
if T is pointer (2) | T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept; T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept; |
-
将原子对象的封装值加 val,并返回原子对象的旧值(适用于整形和指针类型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_add 相当与 std::atomic::operator+=。
-
fetch_sub
if T is integral (1) | T fetch_sub (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; T fetch_sub (T val, memory_order sync = memory_order_seq_cst) noexcept; |
---|---|
if T is pointer (2) | T fetch_sub (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept; T fetch_sub (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept; |
-
将原子对象的封装值减 val,并返回原子对象的旧值(适用于整形和指针类型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_sub 相当与 std::atomic::operator-=。
-
fetch_and
T fetch_and (T val, memory_order sync = memory_order_seq_cst)
volatile
noexcept
;
T fetch_and (T val, memory_order sync = memory_order_seq_cst)
noexcept
;
|
-
将原子对象的封装值按位与 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_add 相当与 std::atomic::operator&=。
-
fetch_or
T fetch_or (T val, memory_order sync = memory_order_seq_cst)
volatile
noexcept
;
T fetch_or (T val, memory_order sync = memory_order_seq_cst)
noexcept
;
|
-
将原子对象的封装值按位或 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_or 相当与 std::atomic::operator|=。
-
fetch_xor
T fetch_xor (T val, memory_order sync = memory_order_seq_cst)
volatile
noexcept
;
T fetch_xor (T val, memory_order sync = memory_order_seq_cst)
noexcept
;
|
-
将原子对象的封装值按位异或 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_xor 相当与 std::atomic::operator^=。
-
operator++
pre-increment (1) | T operator++() volatile noexcept; T operator++() noexcept; |
---|---|
post-increment (2) | T operator++ (int) volatile noexcept; T operator++ (int) noexcept; |
-
自增运算符重载, 第一种形式 (1) 返回自增后的值(即前缀++),第二种形式(2) 返回自增前的值(即后缀++),适用于整形和指针类型的 std::atomic 特化版本。
-
operator--
自减运算符重载, 第一种形式 (1) 返回自减后的值(即前缀--),第二种形式(2) 返回自减前的值(即后缀--),适用于整形和指针类型的 std::atomic 特化版本。
-
atomic::operator (comp. assign.)
复合赋值运算符重载,主要包含以下形式:
if T is integral (1) | T operator+= (T val) volatile noexcept; T operator+= (T val) noexcept; T operator-= (T val) volatile noexcept; T operator-= (T val) noexcept; T operator&= (T val) volatile noexcept; T operator&= (T val) noexcept; T operator|= (T val) volatile noexcept; T operator|= (T val) noexcept; T operator^= (T val) volatile noexcept; T operator^= (T val) noexcept; |
---|---|
if T is pointer (2) | T operator+= (ptrdiff_t val) volatile noexcept; T operator+= (ptrdiff_t val) noexcept; T operator-= (ptrdiff_t val) volatile noexcept; T operator-= (ptrdiff_t val) noexcept; |
以上各个 operator 都会有对应的 fetch_* 操作,详细见下表:
操作符 | 成员函数 | 支持类型 | |||
---|---|---|---|---|---|
复合赋值 | 等价于 | 整型 | 指针类型 | 其他类型 | |
+ | atomic::operator+= | atomic::fetch_add | 是 | 是 | 否 |
- | atomic::operator-= | atomic::fetch_sub | 是 | 是 | 否 |
& | atomic::operator&= | atomic::fetch_and | 是 | 否 | 否 |
| | atomic::operator|= | atomic::fetch_or | 是 | 否 | 否 |
^ | atomic::operator^= | atomic::fetch_xor | 是 | 否 | 否 |
好了,本节先介绍这里,下一节我会介绍 C++11 中 C 风格的原子操作 API。
四
前面三篇文章《C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍)》、《C++11 并发指南六( <atomic> 类型详解二 std::atomic )》、《C++11 并发指南六(atomic 类型详解三 std::atomic (续))》都是采用 C++ 的方式介绍原子对象,本节我会给大家介绍 C++11 原子操作中 C 风格的 API。
总地来说,C++11 标准中规定了两大类原子对象,std::atomic_flag 和 std::atomic,前者 std::atomic_flag 一种最简单的原子布尔类型,只支持两种操作,test-and-set 和 clear。而 std::atomic 是模板类,一个模板类型为 T 的原子对象中封装了一个类型为 T 的值,并且C++11 标准中除了定义基本 std::atomic 模板类型外,还提供了针对整形(integral)和指针类型的特化实现,提供了大量的 API,极大地方便了开发者使用。下面我分别介绍基于 std::atomic_flag 和 std::atomic 的 C 风格 API。
基于 std::atomic_flag 类型的 C 风格 API
-
atomic_flag_test_and_set
bool atomic_flag_test_and_set (volatile atomic_flag* obj) noexcept; bool atomic_flag_test_and_set (atomic_flag* obj) noexcept;
-
检测并设置 std::atomic_flag 的值,并返回 std::atomic_flag 的旧值,和 std::atomic::test_and_set() 成员函数的功能相同,整个过程也是原子的,默认的内存序为
memory_order_seq_cst。
-
atomic_flag_test_and_set_explicit
bool atomic_flag_test_and_set (volatile atomic_flag* obj, memory_order sync) noexcept; bool atomic_flag_test_and_set (atomic_flag* obj, memory_order sync) noexcept;
-
检测并设置 std::atomic_flag 的值,并返回 std::atomic_flag 的旧值,和 std::atomic::test_and_set() 成员函数的功能相同,整个过程也是原子的。sync 参数指定了内存序(Memory Order),可能的取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_flag_clear
void atomic_flag_clear (volatile atomic_flag* obj) noexcept; void atomic_flag_clear (atomic_flag* obj) noexcept;
-
清除 std::atomic_flag 对象,并设置它的值为 false,和 std::atomic::clear() 成员函数的功能相同,整个过程也是原子的,默认的内存序为
memory_order_seq_cst。
-
atomic_flag_clear_explicit
void atomic_flag_clear (volatile atomic_flag* obj, memory_order sync) noexcept; void atomic_flag_clear (atomic_flag* obj, memory_order sync) noexcept;
-
清除 std::atomic_flag 对象,并设置它的值为 false,和 std::atomic::clear() 成员函数的功能相同,整个过程也是原子的,sync 参数指定了内存序(Memory Order),可能的取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
基于 std::atomic 模板类型的 C 风格 API
-
atomic_is_lock_free
template (1) | template <class T> bool atomic_is_lock_free (const volatile atomic<T>* obj) noexcept; template <class T> bool atomic_is_lock_free (const atomic<T>* obj) noexcept; |
---|---|
overloads (2) | bool atomic_is_lock_free (const volatile A* obj) noexcept; bool atomic_is_lock_free (const A* obj) noexcept; |
-
判断该 std::atomic 对象是否具备 lock-free 的特性。如果某个对象满足
lock-free 特性,在多个线程访问该对象时不会导致线程阻塞。(可能使用某种事务内存
transactional memory方法实现 lock-free 的特性)
。
-
atomic_init
template (1) | template <class T> void atomic_init (volatile atomic<T>* obj, T val) noexcept; template <class T> void atomic_init (atomic<T>* obj, T val) noexcept; |
---|---|
overloads (2) | void atomic_init (volatile A* obj, T val) noexcept; void atomic_init (A* obj, T val) noexcept; |
-
初始化原子对象。
val 指定原子对象的初始值。如果对一个已初始化的原子对象再次调用 atomic_init(),则会导致未定义行为(undefined behavior),如果你想修改原子对象的值,应该使用 std::atomic_store();
-
atomic_store
template (1) | template <class T> void atomic_store (volatile atomic<T>* obj, T val) noexcept; template <class T> void atomic_store (atomic<T>* obj, T val) noexcept; |
---|---|
overloads (2) | void atomic_store (volatile A* obj, T val) noexcept; void atomic_store (A* obj, T val) noexcept; |
-
修改原子对象的值,默认的内存序为memory_order_seq_cst。该函数相当于 std::atomic 对象的 store 或者operator=() 成员函数,如果你需要显式指定内存序,应该使用atomic_store_explicit。
-
atomic_store_explicit
修改原子对象的值。该函数相当于 std::atomic 对象的 store 或者operator=() 成员函数
,sync 指定了内存序,可取的参数为:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_release | Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_load
template (1) | template <class T> T atomic_load (const volatile atomic<T>* obj) noexcept; template <class T> T atomic_load (const atomic<T>* obj) noexcept; |
---|---|
overloads (2) | T atomic_load (const volatile A* obj) noexcept; T atomic_load (const A* obj) noexcept; |
-
读取被封装的值,
默认的内存序为memory_order_seq_cst。该函数与 std::atomic 对象的atomic::load()和atomic::operator T() 成员函数等价。
-
atomic_load_explicit
template (1) | template <class T> T atomic_load_explicit (const volatile atomic<T>* obj, memory_order sync) noexcept; template <class T> T atomic_load_explicit (const atomic<T>* obj, memory_order sync) noexcept; |
---|---|
overloads (2) | T atomic_load_explicit (const volatile A* obj, memory_order sync) noexcept; T atomic_load_explicit (const A* obj, memory_order sync) noexcept; |
-
读取被封装的值,参数 sync 设置内存序(Memory Order),可能的取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_seq_cst | Sequentially consistent |
-
该函数与 std::atomic 对象的atomic::load() 成员函数等价。
-
atomic_exchange
template (1) | template <class T> T atomic_exchange (volatile atomic<T>* obj, T val) noexcept; template <class T> T atomic_exchange (atomic<T>* obj, T val) noexcept; |
---|---|
overloads (2) | T atomic_exchange (volatile A* obj, T val) noexcept; T atomic_exchange (A* obj, T val) noexcept; |
-
读取并修改被封装的值,exchange 会将 val 指定的值替换掉之前该原子对象封装的值,并返回之前该原子对象封装的值,整个过程是原子的(因此exchange 操作也称为
read-modify-write 操作)。
该函数与 std::atomic 对象的atomic::exchange() 成员函数等价。
-
atomic_exchange_explicit
template (1) | template <class T> T atomic_store_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept; template <class T> T atomic_store_explicit (atomic<T>* obj, T val, memory_order sync) noexcept; |
---|---|
overloads (2) | T atomic_store_explicit (volatile A* obj, T val, memory_order sync) noexcept; T atomic_store_explicit (A* obj, T val, memory_order sync) noexcept; |
-
读取并修改被封装的值,exchange 会将 val 指定的值替换掉之前该原子对象封装的值,并返回之前该原子对象封装的值,整个过程是原子的(因此exchange 操作也称为
read-modify-write 操作)。sync参数指定内存序(Memory Order),可能的取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_compare_exchange_weak
template (1) | template <class T> bool atomic_compare_exchange_weak (volatile atomic<T>* obj, T* expected, T val) noexcept; template <class T> bool atomic_compare_exchange_weak (atomic<T>* obj, T* expected, T val) noexcept; |
---|---|
overloads (2) | bool atomic_compare_exchange_weak (volatile A* obj, T* expected, T val) noexcept; bool atomic_compare_exchange_weak (A* obj, T* expected, T val) noexcept; |
-
比较并交换被封装的值(weak)与参数
expected 所指定的值是否相等,如果:
- 相等,则用 val 替换原子对象的旧值。
- 不相等,则用原子对象的旧值替换 expected ,因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。
-
该函数通常会读取原子对象封装的值,如果比较为 true(即原子对象的值等于
expected),则替换原子对象的旧值,但整个操作是原子的,在某个线程读取和修改该原子对象时,另外的线程不能对读取和修改该原子对象。
注意,该函数直接比较原子对象所封装的值与参数 expected 的物理内容,所以某些情况下,对象的比较操作在使用 operator==() 判断时相等,但 atomic_compare_exchange_weak 判断时却可能失败,因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3,它们在逻辑上都表示"真",但在物理上两者的表示并不相同)。
与 atomic_compare_exchange_strong 不同, weak 版本的 compare-and-exchange 操作允许( spuriously 地)返回 false(即原子对象所封装的值与参数 expected 的物理内容相同,但却仍然返回 false),不过在某些需要循环操作的算法下这是可以接受的,并且在一些平台下 compare_exchange_weak 的性能更好 。如果 atomic_compare_exchange_weak 的判断确实发生了伪失败( spurious failures)——即使原子对象所封装的值与参数 expected 的物理内容相同,但判断操作的结果却为 false,atomic_compare_exchange_weak 函数返回 false,并且参数
expected 的值不会改变。
-
atomic_compare_exchange_weak_explicit
template (1) | template <class T> bool atomic_compare_exchange_weak_explicit (volatile atomic<T>* obj, T* expected, T val, memory_order success, memory_order failure) noexcept; template <class T> bool atomic_compare_exchange_weak_explicit (atomic<T>* obj, T* expected, T val, memory_order success, memory_order failure) noexcept; |
---|---|
overloads (2) | bool atomic_compare_exchange_weak_explicit (volatile A* obj, T* expected, T val, memory_order success, memory_order failure) noexcept; bool atomic_compare_exchange_weak_explicit (A* obj, T* expected, T val, memory_order success, memory_order failure) noexcept; |
-
比较并交换被封装的值(weak)与参数
expected 所指定的值是否相等,如果:
- 相等,则用 val 替换原子对象的旧值。
- 不相等,则用原子对象的旧值替换 expected ,因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。
-
该函数通常会读取原子对象封装的值,如果比较为 true(即原子对象的值等于
expected),则替换原子对象的旧值,但整个操作是原子的,在某个线程读取和修改该原子对象时,另外的线程不能对读取和修改该原子对象。
内存序(Memory Order)的选择取决于比较操作结果,如果比较结果为 true(即原子对象的值等于 expected),则选择参数 success 指定的内存序,否则选择参数 failure 所指定的内存序。
注意,该函数直接比较原子对象所封装的值与参数 expected 的物理内容,所以某些情况下,对象的比较操作在使用 operator==() 判断时相等,但 compare_exchange_weak 判断时却可能失败,因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3,它们在逻辑上都表示"真",但在物理上两者的表示并不相同)。
与 atomic_compare_exchange_strong 不同, weak 版本的 compare-and-exchange 操作允许( spuriously 地)返回 false(即原子对象所封装的值与参数 expected 的物理内容相同,但却仍然返回 false),不过在某些需要循环操作的算法下这是可以接受的,并且在一些平台下 compare_exchange_weak 的性能更好 。如果 atomic_compare_exchange_weak 的判断确实发生了伪失败( spurious failures)——即使原子对象所封装的值与参数 expected 的物理内容相同,但判断操作的结果却为 false,atomic_compare_exchange_weak函数返回 false,并且参数
expected 的值不会改变。
-
对于某些不需要采用循环操作的算法而言, 通常采用
atomic_compare_exchange_strong更好。另外,该函数的内存序由 sync 参数指定,可选条件如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_compare_exchange_strong
template (1) | template <class T> bool atomic_compare_exchange_strong (volatile atomic<T>* obj, T* expected, T val) noexcept; template <class T> bool atomic_compare_exchange_strong (atomic<T>* obj, T* expected, T val) noexcept; |
---|---|
overloads (2) | bool atomic_compare_exchange_strong (volatile A* obj, T* expected, T val) noexcept; bool atomic_compare_exchange_strong (A* obj, T* expected, T val) noexcept; |
-
比较并交换被封装的值(strong)与参数
expected 所指定的值是否相等,如果:
- 相等,则用 val 替换原子对象的旧值。
- 不相等,则用原子对象的旧值替换 expected ,因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。
-
该函数通常会读取原子对象封装的值,如果比较为 true(即原子对象的值等于
expected),则替换原子对象的旧值,但整个操作是原子的,在某个线程读取和修改该原子对象时,另外的线程不能对读取和修改该原子对象。
注意,该函数直接比较原子对象所封装的值与参数 expected 的物理内容,所以某些情况下,对象的比较操作在使用 operator==() 判断时相等,但 compare_exchange_weak 判断时却可能失败,因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3,它们在逻辑上都表示"真",但在物理上两者的表示并不相同)。
-
与 atomic_
compare_exchange_weak 不同, strong版本的 compare-and-exchange 操作不允许(
spuriously 地)返回 false,即原子对象所封装的值与参数
expected 的物理内容相同,比较操作一定会为 true。不过在某些平台下,如果算法本身需要循环操作来做检查, atomic_compare_exchange_weak 的性能会更好。
因此对于某些不需要采用循环操作的算法而言, 通常采用 atomic_ compare_exchange_strong 更好。
-
atomic_compare_exchange_strong_explicit
template (1) | template <class T> bool atomic_compare_exchange_strong_explicit (volatile atomic<T>* obj, T* expected, T val, memory_order success, memory_order failure) noexcept; template <class T> bool atomic_compare_exchange_strong_explicit (atomic<T>* obj, T* expected, T val, memory_order success, memory_order failure) noexcept; |
---|---|
overloads (2) | bool atomic_compare_exchange_strong_explicit (volatile A* obj, T* expected, T val, memory_order success, memory_order failure) noexcept; bool atomic_compare_exchange_strong_explicit (A* obj, T* expected, T val, memory_order success, memory_order failure) noexcept; |
-
比较并交换被封装的值(strong)与参数
expected 所指定的值是否相等,如果:
- 相等,则用 val 替换原子对象的旧值。
- 不相等,则用原子对象的旧值替换 expected ,因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。
-
该函数通常会读取原子对象封装的值,如果比较为 true(即原子对象的值等于
expected),则替换原子对象的旧值,但整个操作是原子的,在某个线程读取和修改该原子对象时,另外的线程不能对读取和修改该原子对象。
内存序(Memory Order)的选择取决于比较操作结果,如果比较结果为 true(即原子对象的值等于 expected),则选择参数 success 指定的内存序,否则选择参数 failure 所指定的内存序。
注意,该函数直接比较原子对象所封装的值与参数 expected 的物理内容,所以某些情况下,对象的比较操作在使用 operator==() 判断时相等,但 compare_exchange_weak 判断时却可能失败,因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3,它们在逻辑上都表示"真",但在物理上两者的表示并不相同)。
-
与atomic_
compare_exchange_weak 不同, strong版本的 compare-and-exchange 操作不允许(
spuriously 地)返回 false,即原子对象所封装的值与参数
expected 的物理内容相同,比较操作一定会为 true。不过在某些平台下,如果算法本身需要循环操作来做检查, atomic_compare_exchange_weak 的性能会更好。
因此对于某些不需要采用循环操作的算法而言, 通常采用 atomic_ compare_exchange_strong 更好。另外,该函数的内存序由 sync 参数指定,可选条件如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_fetch_add
template (integral) (1) | template <class T> T atomic_fetch_add (volatile atomic<T>* obj, T val) noexcept; template <class T> T atomic_fetch_add (atomic<T>* obj, T val) noexcept; |
---|---|
template (pointer) (2) | template <class U> U* atomic_fetch_add (volatile atomic<U*>* obj, ptrdiff_t val) noexcept; template <class U> U* atomic_fetch_add (atomic<U*>* obj, ptrdiff_t val) noexcept; |
overloads (3) | T atomic_fetch_add (volatile A* obj, M val) noexcept; T atomic_fetch_add (A* obj, M val) noexcept; |
-
将原子对象的封装值加 val,并返回原子对象的旧值(适用于整形和指针类型的 std::atomic 特化版本),整个过程是原子的。该函数默认内存序为 memory_order_seq_cst。
该函数等价于 std::atomic 对象的atomic::fetch_add和 atomic::operator+= 成员函数。
-
atomic_fetch_add_explicit
template (integral) (1) | template <class T> T atomic_fetch_add_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept; template <class T> T atomic_fetch_add_explicit (atomic<T>* obj, T val, memory_order sync) noexcept; |
---|---|
template (pointer) (2) | template <class U> U* atomic_fetch_add_explicit (volatile atomic<U*>* obj, ptrdiff_t val, memory_order sync) noexcept; template <class U> U* atomic_fetch_add_explicit (atomic<U*>* obj, ptrdiff_t val, memory_order sync) noexcept; |
overloads (3) | T atomic_fetch_add_explicit (volatile A* obj, M val, memory_order sync) noexcept; T atomic_fetch_add_explicit (A* obj, M val, memory_order sync) noexcept; |
-
将原子对象的封装值加 val,并返回原子对象的旧值(适用于整形和指针类型的 std::atomic 特化版本),整个过程是原子的。
该函数等价于 std::atomic 对象的atomic::fetch_add成员函数。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_fetch_sub
template (integral) (1) | template <class T> T atomic_fetch_sub (volatile atomic<T>* obj, T val) noexcept; template <class T> T atomic_fetch_sub (atomic<T>* obj, T val) noexcept; |
---|---|
template (pointer) (2) | template <class U> U* atomic_fetch_sub (volatile atomic<U*>* obj, ptrdiff_t val) noexcept; template <class U> U* atomic_fetch_sub (atomic<U*>* obj, ptrdiff_t val) noexcept; |
overloads (3) | T atomic_fetch_sub (volatile A* obj, M val) noexcept; T atomic_fetch_sub (A* obj, M val) noexcept; |
-
将原子对象的封装值减 val,并返回原子对象的旧值(适用于整形和指针类型的 std::atomic 特化版本),整个过程是原子的。
-
atomic_fetch_sub_explicit
template (integral) (1) | template <class T> T atomic_fetch_sub_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept; template <class T> T atomic_fetch_sub_explicit (atomic<T>* obj, T val, memory_order sync) noexcept; |
---|---|
template (pointer) (2) | template <class U> U* atomic_fetch_sub_explicit (volatile atomic<U*>* obj, ptrdiff_t val, memory_order sync) noexcept; template <class U> U* atomic_fetch_sub_explicit (atomic<U*>* obj, ptrdiff_t val, memory_order sync) noexcept; |
overloads (3) | T atomic_fetch_sub_explicit (volatile A* obj, M val, memory_order sync) noexcept; T atomic_fetch_sub_explicit (A* obj, M val, memory_order sync) noexcept; |
-
将原子对象的封装值减 val,并返回原子对象的旧值(适用于整形和指针类型的 std::atomic 特化版本),整个过程是原子的。
-
该函数等价于 std::atomic 对象的atomic::fetch_sub成员函数。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_fetch_and
emplate (integral) (1) | template <class T> T atomic_fetch_and (volatile atomic<T>* obj, T val) noexcept; template <class T> T atomic_fetch_and (atomic<T>* obj, T val) noexcept; |
---|---|
overloads (2) | T atomic_fetch_and (volatile A* obj, T val) noexcept; T atomic_fetch_and (A* obj, T val) noexcept; |
-
将原子对象的封装值按位与 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。
-
atomic_fetch_and_explicit
template (integral) (1) | template <class T> T atomic_fetch_and_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept; template <class T> T atomic_fetch_and_explicit (atomic<T>* obj, T val, memory_order sync) noexcept; |
---|---|
overloads (2) | T atomic_fetch_and_explicit (volatile A* obj, T val, memory_order sync) noexcept; T atomic_fetch_and_explicit (A* obj, T val, memory_order sync) noexcept; |
-
将原子对象的封装值按位与 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。
-
该函数等价于 std::atomic 对象的atomic::fetch_and成员函数。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_fetch_or
template (integral) (1) | template <class T> T atomic_fetch_or (volatile atomic<T>* obj, T val) noexcept; template <class T> T atomic_fetch_or (atomic<T>* obj, T val) noexcept; |
---|---|
overloads (2) | T atomic_fetch_or (volatile A* obj, T val) noexcept; T atomic_fetch_or (A* obj, T val) noexcept; |
-
将原子对象的封装值按位或 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。
-
atomic_fetch_or_explicit
template (integral) (1) | template <class T> T atomic_fetch_or_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept; template <class T> T atomic_fetch_or_explicit (atomic<T>* obj, T val, memory_order sync) noexcept; |
---|---|
overloads (2) | T atomic_fetch_or_explicit (volatile A* obj, T val, memory_order sync) noexcept; T atomic_fetch_or_explicit (A* obj, T val, memory_order sync) noexcept; |
-
A
将原子对象的封装值按位或 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。
-
该函数等价于 std::atomic 对象的atomic::fetch_or成员函数。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
-
atomic_fetch_xor
template (integral) (1) | template <class T> T atomic_fetch_xor (volatile atomic<T>* obj, T val) noexcept; template <class T> T atomic_fetch_xor (atomic<T>* obj, T val) noexcept; |
---|---|
overloads (2) | T atomic_fetch_xor (volatile A* obj, T val) noexcept; T atomic_fetch_xor (A* obj, T val) noexcept; |
-
将原子对象的封装值按位异或 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。
-
atomic_fetch_xor_explicit
template (integral) (1) | template <class T> T atomic_fetch_xor_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept; template <class T> T atomic_fetch_xor_explicit (atomic<T>* obj, T val, memory_order sync) noexcept; |
---|---|
overloads (2) | T atomic_fetch_xor_explicit (volatile A* obj, T val, memory_order sync) noexcept; T atomic_fetch_xor_explicit (A* obj, T val, memory_order sync) noexcept; |
-
将原子对象的封装值按位异或 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。
-
该函数等价于 std::atomic 对象的atomic::fetch_xor成员函数。sync 参数指定内存序:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
与原子对象初始化相关的宏
-
此外,还有两个宏值得关注,他们分别是:
-
ATOMIC_VAR_INIT(val)
初始化 std::atomic 对象。
-
ATOMIC_FLAG_INIT
初始化 std::atomic_flag 对象。