std::atomic 使用
std::atomic(原子变量)
参考文章包括:
C++原子变量atomic详解 - 知乎 (zhihu.com)
C++ 中的原子变量(std::atomic)使用指南_std::atomic-CSDN博客
cplusplus.com/reference/atomic/atomic/
原子变量是C++ 11中用于多线程编程的便捷工具(同步机制)之一.其提供了一种线程安全的方式来访问和修改共享数据(原子操作),从而无需显式的调用互斥锁,避免了竞态条件(race condition)和死锁(deadlock)等问题.
std::atomic 支持各种数据类型,如整数,布尔值,指针等,通过std::atomic<T>定义:
std::atomic<int> atomicInt(0);
std::atomic<bool> atomicBool(false);
std::atomic<double> *atomicString = new std::atomic<double>(3.1415);
is_lock_free函数:
bool is_lock_free() const volatile noexcept;
bool is_lock_free() const noexcept;
返回当前atomic对象是否支持无锁操作,是则返回true,反之返回false;
#include <iostream>
#include <atomic>
int main(int argc,char**argv)
{
std::atomic<int> x(10);
std::cout << std::boolalpha
<< "std::atomic<int> is_lock_free ?\n"
<< x.is_lock_free()
<< std::endl;
return 0;
}
输出:
std::atomic<int> is_lock_free ?
true
load函数:
T load(memory_order sync = memory_order_seq_cst) const volatile noexcept;
T load(memory_order sync = memory_order_seq_cst) const noexcept;
operator T() const noexcept;
用于获取原子变量的当前值.
load函数的参数memory_order表示内存序,也就是对原子变量的读操作要遵循哪种内存模型。C++中定义了多种内存序,包括:
- memory_order_relaxed:最轻量级的内存序,不提供任何同步机制。
- memory_order_acquire:在本线程中,所有后面的读写操作必须在这个操作之后执行。
- memory_order_release:在本线程中,该操作之前的所有读写操作必须在这个操作之前执行。
- memory_order_seq_cst:最严格的内存序,保证所有线程看到的读写操作的顺序都是一致的。
使用load函数时,如果不指定memory_order,则默认为memory_order_seq_cst。
load函数的返回值类型为T,即原子变量的类型。在使用load函数时需要指定类型参数T。如果使用第二种形式的load函数,则无需指定类型参数T,程序会自动根据上下文推断出类型。
store函数:
void store(T desired, std::memory_order order = std::memory_order_seq_cst) volatile noexcept;
void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept;
用于将给定的值存储到该原子变量中(desired 为要存储的值)
#include <iostream>
#include <atomic>
int main(int argc,char**argv)
{
std::atomic<int> x(10);
std::cout << x.load(std::memory_order_relaxed)
<< std::endl;
x.store(22,std::memory_order_relaxed);
std::cout << x.load(std::memory_order_relaxed)
<< std::endl;
return 0;
}
输出:
10
22
exchange函数:
template< class T >
T exchange( volatile std::atomic<T>* obj, T desired );
访问和修改包含的值.
obj指向需要替换值的atomic对象,desired为期望被替换成的值,若替换成功,返回原来被替换的值.
此函数整体为原子操作,直到返回值的那一刻,都不受其他线程影响.
#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;
}
输出:
spawning 10 threads that count to 1 million...
thread #4 won!
compare_exchange_strong / compare_exchange_weak函数:
bool compare_exchange_strong(T& expected, T desired, memory_order success = memory_order_seq_cst, memory_order failure = memory_order_seq_cst) noexcept;
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;
两者的作用都是比较一个值与期望值是否相等,相等时替换为新值,strong会保证操作的原子性(expected:期望值,desired:新值,success:执行成功时执行的内存序列的类型,同failure).weak版本可能会在某些平台上出现失败并重试.
#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* 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<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;
}
输出:
4 0 5 2 3 8 9 7 6 1
fetch系列专业化支持:
fetch_add | 添加到包含的值并返回它在操作之前具有的值 |
---|---|
fetch_sub | 从包含的值中减去,并返回它在操作之前的值。 |
fetch_and | 读取包含的值,并将其替换为在读取值和 之间执行按位 AND 运算的结果。 |
fetch_or | 读取包含的值,并将其替换为在读取值和 之间执行按位 OR 运算的结果。 |
fetch_xor | 读取包含的值,并将其替换为在读取值和 之间执行按位 XOR 运算的结果。 |