- atomic types是可以封装值的类型,保证不产生数据竞争,可用于多线程间内存访问的同步。
1.类classes
1.1 atomic
- 保证多线程的访问不会导致数据竞争
atmoic对象通过指定不同的memory orders可以保证其所在线程的其他non-atomic对象的访问同步。
template struct atomic;
- 1)构造函数
//default (1)
atomic() noexcept = default;
//initialization (2)
constexpr atomic (T val) noexcept;
//copy [deleted] (3)
atomic (const atomic&) = delete;
// constructing atomics
#include // std::cout
#include // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT
#include // std::thread, std::this_thread::yield
#include // std::vector
std::atomic ready (false);
std::atomic_flag winner = ATOMIC_FLAG_INIT;
void count1m (int id) {
while (!ready) { std::this_thread::yield(); } // wait for the ready signal
for (volatile int i=0; i<1000000; ++i) {} // go!, count to 1 million
if (!winner.test_and_set()) { std::cout << "thread #" << id << " won!\n"; }
};
int main ()
{
std::vector 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;
}
- 2)赋值运算
该操作是原子的,使用顺序一致性(memory_order_seq_cst)。
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;
3)is_lock_free
检查对象是否没有加锁。4)store
该操作是原子的,memory ordering由sync指定。
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;
- 5)load
T load (memory_order sync = memory_order_seq_cst) const volatile noexcept;
T load (memory_order sync = memory_order_seq_cst) const noexcept;
- 6)operator T
operator T() const volatile noexcept;
operator T() const noexcept;
//使用实例
std::atomic foo = 0;
std::atomic bar = 0;
bar = static_cast(foo);
- 7)exchange
将值替换为val并返回之前的值。
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;
// atomic::exchange example
#include // std::cout
#include // std::atomic
#include // std::thread
#include // std::vector
std::atomic ready (false);
std::atomic 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 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;
}
- 8)compare_exchange_weak
比较atomic对象封装的值和expected的值:
a.如果为true,则将val的值存入atomic对象
b.如果为false,则将atomic里面的值赋值给expected
版本2根据比较结果指定了memory order,true选择success,false选择failure。
weak版本可能会发生伪失败,即相等时也会返回false。这是可以接受的,因为并没有修改expected。用在循环中,在某些平台上可能显著提高性能。
对于非循环算法,优先使用strong版本。
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;
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;
// atomic::compare_exchange_weak example:
#include // std::cout
#include // std::atomic
#include // std::thread
#include // std::vector
// a simple global linked list:
struct Node { int value; Node* next; };
std::atomic list_head (nullptr);
void append (int val) { // append an element to the list
Node* oldHead = list_head;
Node* newNode = new Node {val,oldHead};
// what follows is equivalent to: list_head = newNode, but in a thread-safe way:
while (!list_head.compare_exchange_weak(oldHead,newNode))
newNode->next = oldHead;
}
int main ()
{
// spawn 10 threads to fill the linked list:
std::vector 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)compare_exchange_strong
不允许伪失败
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;
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;
-
10)特定基础类型支持的算术运算
fetch_add +=
fetch_sub -=
fetch_and &=
fetch_or |=
fetch_xor ^=
operator++/--
//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;
//pre-increment (1)
T operator++() volatile noexcept;
T operator++() noexcept;
//post-increment (2)
T operator++ (int) volatile noexcept;
T operator++ (int) noexcept;
1.2 atomic_flag
- atomic flags是lock-free
struct atomic_flag;
- 1)构造函数
默认初始化的状态是不定的。除非初始化为宏ATOMIC_FLAT_INIT。此时保证为clear状态。
atomic_flag() noexcept = default;
atomic_flag (const atomic_flag&T) = delete;
- 2)test_and_set
set atomic_flag,并且返回调用该函数之前atomic_flag是否被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;
// atomic_flag as a spinning lock
#include // std::cout
#include // std::atomic_flag
#include // std::thread
#include // std::vector
#include // 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 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();
return 0;
}
- 3)clear
clear atomic_flag
void clear (memory_order sync = memory_order_seq_cst) volatile noexcept;
void clear (memory_order sync = memory_order_seq_cst) noexcept;
2.类型types
2.1 memory_order
- 作为执行原子操作函数的参数,用于指定其他线程操作进行同步的方式。
- 线程可能在atomic对象以外的内存位置进行操作。memory_order就是用来指定在线程间如何同步可见副作用。
- memory_order_relaxed对线程见的内存访问不做任何保证;
memory_order_consume,用于load操作,当所有对依赖释放操作的释放线程中内存访问发生后(对load线程由可见的副作用),该操作可以执行。
memory_order_acquire,用于load操作。同上。
memory_order_release,用于store操作。该操作在consume或者acquire之前发生。作为其他可能对load线程产生可见副作用的线程访问内存的同步点。
memory_order_acq_rel,用于load/store操作。loads acquiring / stores releasing。
memory_order_seq_cst,操作按照顺序一致的方式执行。当所有对其他线程产生可见副作用的内存访问已经发生后,这种类型的操作才安排执行。这是最严格的内存顺序,通过非原子内存访问保证线程交互之间最小的意外副作用。
typedef enum 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;
2.2 C-style atomic types
3.函数
3.1 函数
- kill_dependency
返回y的值,并且不保持任何依赖。
使用memory_order_consume作为内存顺序的原子操作要求编译器检查 访问用于生成释放值的内存位置 所带来的依赖性。 同步这些携带的依赖关系可能导致某些硬件fence被设置并迫使编译器放弃涉及这些存储器位置的某些潜在优化。
template
T kill_dependency (T y) noexcept;
- atomic_thread_fence
建立多线程fence:对此函数的调用点将成为acquire或release同步点(或两者)。
extern "C" void atomic_thread_fence (memory_order sync) noexcept;
- atomic_signal_fence同上
extern "C" void atomic_signal_fence (memory_order sync) noexcept;
3.2 Functions for atomic objects (C-style)
-
同atomic对应的成员函数功能一样
3.3 Functions for atomic flags (C-style)
-
同atomic_flag成员函数功能相同
4.宏
4.1 宏函数
- ATOMIC_VAR_INIT为了保持与C实现的兼容
- ATOMIC_FLAG_INIT,初始化atomic flag为clear状态