atomic使用
- 概述
- 介绍
- 使用场景
- 头文件
- atomic的使用
- 创建
- load()
- store()
- exchange()
- compare_exchange_weak
- compare_exchange_strong()
- fetch_add()
- fetch_sub()
- fetch_and()
- fetch_or()
- fetch_xor()
- 示例
- 实现代码
- 运行结果
概述
本文只要讲述C++11中atomic的使用,并通过示例来进一步的说明。
介绍
atomic在c++11中被引入,作为一种原子操作,之所以是原子操作,是因为其能保证操作过程不会被打断或终止,保证了被操作数据的安全性。原子操作是线程安全的。尤其在多线程同步中,常用于基本类型共享数据的保护。
原子操作:指的是不可被中断的一个或一系列操作,这些操作要么全部执行成功,要么全部不执行。
使用场景
-
并发计数器:在多线程环境下,如果多个线程对同一个变量进行自增操作,可能会导致数据不一致。使用atomic可以确保自增操作的原子性,从而确保计数的准确性。
-
读取和修改共享变量:当多个线程同时读取和修改同一个共享变量时,如果不使用原子操作,可能会导致数据冲突。使用atomic可以确保读取和修改操作的原子性,避免数据不一致。
-
信号量:在多线程编程中,信号量常用于线程间的同步和通信。使用atomic可以确保对信号量的操作是原子的,从而避免出现竞态条件。
头文件
使用时需要头文件:
#include<atomic>
atomic的使用
创建
atomic是一个模板类型,可以用于基本数据类型的原子操作。非基本数据类型需要重新去实现一系列的复杂操作,不建议这样做。
以创建int型变量为例:
atomic<int> nAtomic;
此时nAtomic里的值为默认值0。
load()
实际上就是取出nAtomic的值:
nAtomic.load();//0
store()
向nAtomic变量中存储一个数值:
nAtomic.store(3);
exchange()
以原子方式交换变量的值,并返回旧值。
int nOld = nAtomic.exchange(5);
compare_exchange_weak
用于比较和交换两个数值。当nAtomic的当前值与期望值相等,更新为新值,否则,保持nAtomic中的值不变。
int expired = 23;
int newValue = 6;
if (nAtomic.compare_exchange_weak(expired, newValue)) {//true当前值与期望值相等,设置为新值
cout << "当前值与期望值相等,更新新值成功,g_countAtomic = " << nAtomic<< endl;
}
else {//当前值与期望值不想等,还是原来的值
cout << "当前值与期望值不相等,没有更新新值,g_countAtomic = " << nAtomic<< endl;
}
compare_exchange_strong()
与compare_exchange_weak()功能一样。区别是:
-
阻塞:如果比较和交换操作失败,函数通常会忙等待(busy-wait)直到条件满足为止。这意味着它会持续检查条件,直到可以安全地执行比较和交换为止。这种忙等待行为可能会导致 CPU 资源的浪费,因此在高并发场景中可能需要谨慎使用。
-
一致性保证:与 compare_exchange_weak() 相比,compare_exchange_strong() 提供更强的内存一致性保证。具体来说,它确保了在比较和交换操作成功执行后,其他线程将立即看到更新后的值。这是通过强制使用特定的内存排序规则来实现的,这些规则确保了在多线程环境中的可见性和顺序性。
当多线程环境下,一般使用compare_exchange_weak()在一个循环中,确保没有被虚假的比较和交换失败。
下面是一个使用循环的例子:
#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>
std::atomic<int> counter(0); // 定义一个原子整数
void increment_counter() {
int local_copy = counter.load(); // 读取当前值
while (true) {
int new_value = local_copy + 1; // 计算新值
if (counter.compare_exchange_weak(local_copy, new_value)) {
// 如果比较和交换成功,跳出循环
break;
}
// 如果比较和交换失败,重新读取当前值并继续循环
}
}
int main() {
const int num_threads = 10; // 使用10个线程来增加计数器
std::thread threads[num_threads];
// 创建并启动线程
for (int i = 0; i < num_threads; ++i) {
threads[i] = std::thread(increment_counter);
}
// 等待所有线程完成
for (int i = 0; i < num_threads; ++i) {
threads[i].join();
}
// 输出最终计数器的值
std::cout << "Final counter value: " << counter << std::endl;
return 0;
}
如果 compare_exchange_weak() 失败(即返回 false),说明有其他线程在此期间修改了 counter 的值。此时,local_copy 仍然保持原来的值,而 counter 的值已经被其他线程更新。因此,函数重新读取 counter 的当前值到 local_copy 中,并继续循环,尝试再次进行比较和交换。
fetch_add()
原子的增减变量的值,并返回旧值。
int oldResult = nAtomic.fetch_add(7);
fetch_sub()
原子的减少变量的值,并返回旧值。
int oldFet = nAtomic.fetch_sub(4);
fetch_and()
原子的进行按位与,返回操作之前的值。
int oldAndValue = nAtomic.fetch_and(10);
fetch_or()
原子的进行按位或,返回操作之前的值。
int oldOrValue = nAtomic.fetch_or(10);
fetch_xor()
原子的进行按位异或,返回操作之前的值。
int oldXorValue = nAtomic.fetch_xor(13);
示例
下面是一个简单的使用atomic的程序。
实现代码
#include <iostream>
#include <atomic>
using namespace std;
atomic<int> g_countAtomic;
void fun() {
if (!g_countAtomic.load()) {
g_countAtomic.store(2);
cout << "加载atomic的值:" <<g_countAtomic<< endl;
}
g_countAtomic.store(3);
cout << "再次加载atomic的值:" << g_countAtomic << endl;
int nOld = g_countAtomic.exchange(5);
cout << "以前的值:"<<nOld<<" 再次加载atomic的值:" << g_countAtomic.load() << endl;
int expired = 23;
int newValue = 6;
if (g_countAtomic.compare_exchange_weak(expired, newValue)) {//true当前值与期望值相等,设置为新值
cout << "当前值与期望值相等,更新新值成功,g_countAtomic = " << g_countAtomic << endl;
}
else {//当前值与期望值不想等,还是原来的值
cout << "当前值与期望值不相等,没有更新新值,g_countAtomic = " << g_countAtomic << endl;
}
int oldResult = g_countAtomic.fetch_add(7);
cout << "以前的值:" << oldResult << " 增加7后atomic的值:" << g_countAtomic.load() << endl;
int oldFet = g_countAtomic.fetch_sub(4);
cout << "以前的值:" << oldFet << " 减去4后atomic的值:" << g_countAtomic.load() << endl;
int oldAndValue = g_countAtomic.fetch_and(10);//1010
cout << "按位与之前的值:" << oldAndValue <<" 按位与之后的值:"<<g_countAtomic.load() << endl;
int oldOrValue = g_countAtomic.fetch_or(10);
cout << "按位或之前的值:" << oldOrValue << " 按位或之后的值:" << g_countAtomic.load() << endl;
int oldXorValue = g_countAtomic.fetch_xor(13);
cout << "按位异或之前的值:" << oldXorValue << " 按位异或之后的值:" << g_countAtomic.load() << endl;
}
int main()
{
fun();
cout << "----------------------------------" << endl;
return 0;
}