C++ 11原子变量

news/2025/2/15 14:26:28/

原子变量

  • 原子变量的解释
  • 原子变量类 atomic
    • 禁止拷贝
    • 赋值和读取操作
      • 通过函数
      • 特化类型
    • 使用
      • 初始化
      • 赋值
      • 读取
      • 操作对象(指针访问)
    • 使用原子变量解决乱序问题
    • 内存循序约束

原子变量的解释

原子作为长期最小的单位,在这里被定义为不可分割的最小操作,原子性也别定义为不可再分的操作,在C\C++ 中由于程序执行的非常的快,指令可能出现乱序执行的情况(不能保证指令执行的循序和编写程序循序的一致),有编译器的优化,有执行上的优化,总之是为了让程序执行更快导致的特性,这在单线程中几乎不会出现什么问题可以让程序飞快的执行,但是到了多线程的时代就会有问题,实际上就是线程同步上的问题,于是C++11(Java 在2004 年的java5就支持了C++晚了非常久)增加原子变量来实现原子操作。

原子变量类 atomic

原子变量类 可以管理 一个int 变量或者一个指针变量 实现对他的原子操作,注意只能支持 管理非浮点型 变量 或者指针变量
原子变量有以下特性

禁止拷贝

根据原子变量内部的定义

      atomic() = default;~atomic() noexcept = default;atomic(const atomic&) = delete;atomic& operator=(const atomic&) = delete;atomic& operator=(const atomic&) volatile = delete;

可以知道原子变量是不能被拷贝的

赋值和读取操作

通过函数

void store(值,内存顺序约束); //写入一个值
T load(内存顺序约束);//读取值
T exchange(值,内存顺序约束); //写入并且返回原来的值
fetch_add() 相当于 operator +
fetch_sub ()相当于 operator -
fetch_and ()相当于 operator & 不支持指针变量
fetch_or ()相当于 operator | 不支持指针变量
fetch_xor()相当于 operator ~ 不支持指针变量

函数名运算符是否支持指针变量
fetch_add()operator +支持
fetch_sub ()operator -支持
fetch_and ()operator&不支持
fetch_or ()operator |不支持
fetch_xor()operator ~不支持

特化类型

C++给常用的类型进行一些typedef减少编写定义的长度如:

特化类型原始定义
atomic_intstd::atmic<int>
atomic_boolstd::atmic<bool>
atomic_uintstd::atmic<uint>
atomic_longstd::atmic<long>
atomic_charstd::atmic<char>

…还有非常多可以根据使用的时候根据前缀让IDE补全

使用

初始化

int main(void)
{atomic<int> a(1); //通过构造函数初始化atomic<int >b;b  = 1; //通过重载运算符进行初始化, 不建议因为一开始没有初始化内部是未定义状态atomic<int >c;atomic_init(&c,3);//使用atomic_init进行初始化}

赋值

int main(void)
{atomic<int> a(1); //通过构造函数初始化a.store(123); //直接赋值int t = a.exchange(333);//交换旧值cout<<t<<endl;
}

读取

int main(void)
{atomic<int> a(1); //通过构造函数初始化a.store(123);cout<<a.load()<<endl;
}

操作对象(指针访问)

#include <queue>
#include <condition_variable>
#include <atomic>
using namespace  std;
using namespace chrono;class  A
{
public:A(){a =1; }inline int get(){return  a;}void set(int _a){a = _a;}
private:int a;};
int main(void)
{atomic<A *> a(new A()); //通过构造函数初始化auto t = a.load(); //获取对象t->set(1);t->get();auto  t2  = a.exchange(new A());// 重新设置delete t2;auto t3 = a.load();a.store(new A());delete t3;delete a.load();}

使用原子变量解决乱序问题

一个简单的例子,这里最终a的结果不一定是 2000000 考虑编译器的优化以及乱序执行 要么直接等于1000000(编译器检测到只做加法直接优化成等于1000000)要么是一个小于2000000的值(也有一定概率等于2000000)

#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <atomic>
using namespace  std;
using namespace chrono;class  A
{
public:A(){a = 0;}void thread1(){for(int i = 0;i<1000000;i++){++a;}}void thread2(){for(int i = 0;i<1000000;i++){++a;}}int a;};
int main(void)
{A a;thread thread1(&A::thread1,&a);thread thread2(&A::thread2,&a);thread1.join();thread2.join();cout<<a.a <<endl;
}

使用原子变量对他优化

    atomic<int>  a;

再次运行就会稳定输出 2000000

内存循序约束

前面介绍API的时候有些到这个参数,但是一直没有使用,原因是API中默认有一个参数 就是memory_order_seq_cst 保证内存序列一致

类型作用
memory_order_relaxed不做任何限制
memory_order_release保证写入内存之前的操作在它的执行前是完成的(不保证后面的会不会在它前面)
memory_order_acquire保证读取数据之后的的操作在读取他之后是完成的(不保证他前面的会不会在它后面)
memory_order_consume类似memory_order_acquire 当数据发布会收到订阅所以效率稍高
memory_order_acq_rel保证读写内存一致性 也就是 memory_order_release 和memory_order_acquire 结合
memory_order_seq_cst保证顺序的一致性保证前后操作的循序均一致

对 memory_order_consume的补充
这里有一个例子 consumer 函数在等待赋值此时能保证他里面的内容一直是“Hello” 但是不能保证Data的数据一直42

#include <thread>
#include <atomic>
#include <cassert>
#include <string>
std::atomic<std::string*> ptr;
int data;
std::atomic<int> atomic_a;
void producer()
{std::string* p  = new std::string("Hello");  //Adata = 42;//Bptr.store(p, std::memory_order_release); //C
}void consumer()
{std::string* p2;while (!(p2 = ptr.load(std::memory_order_consume))) //D;assert(*p2 == "Hello"); // E    never fires: *p2 carries dependency from ptrassert(data == 42); // F   may or may not fire: data does not carry dependency from ptr
}int main()
{std::thread t1(producer);std::thread t2(consumer);t1.join(); t2.join();
}

http://www.ppmy.cn/news/1572261.html

相关文章

day51 第十一章:图论part02

99. 岛屿数量 深搜 每一块的上下左右都遍历过了之后&#xff0c;这块陆地就遍历完了。是深搜&#xff0c;不是广搜 深搜&#xff1a;递归 def dfs(): if .....: 终止条件 dfs(子节点) directions [[0,1],[1,0],[0,-1],[-1,0]]def dfs(grid, visited, x, y):if grid[x][y]…

微流控专题 | 单细胞封装背景

为了从单细胞实验中获得有意义的生物数据&#xff0c;需要对大量单个细胞进行分析。这可以通过使用基于压力驱动流控的液滴微流控技术来实现。 本应用说明中描述的微流体平台用于生成皮升大小的微液滴&#xff0c;以单细胞水平封装 HeLa 细胞以供进一步分析。此过程称为单细胞封…

kkFileView二开之pdf转图片接口

kkFileView二开之Pdf转图片接口 1 kkFileView源码下载及编译2 Pdf转图片接口2.1 背景2.2 分析2.2 接口开发2.2.1 编写Pdf转图片方法2.2.2 编写转换接口 2.3 接口测试2.3.1 Pdf文件准备2.3.2 pdf2Image 3 部署 1 kkFileView源码下载及编译 前文 【kkFileView二开之源码编译及部…

Dify 框架连接 PGSQL 数据库与 Sandbox 环境下的 Linux 系统调用权限问题

Dify 框架连接 PGSQL 数据库与 Sandbox 环境下的 Linux 系统调用权限问题 背景 在使用 Dify 框架进行开发时&#xff0c;遇到了两个主要的技术挑战&#xff1a; 代码节点连接到 PGSQL&#xff08;PostgreSQL&#xff09;数据库。解决沙盒环境中由于系统调用限制导致的“oper…

左移架构 -- 从攒批,湖仓到使用数据流的实时数据产品

编辑导读: 这篇文章翻译自 Kai Waehner的 《The Shift Left Architecture – From Batch and Lakehouse to Real-Time Data Products with Data Streaming》。文章通过数据产品的概念引出了如何创建可重复使用的数据产品使企业能够从当前和未来的数据中获得价值。基于构建数据产…

Hyperledger Fabric 入门笔记(十八)Fabric V2.5 测试网络部署补充 - 排序节点管理

文章目录 一、准备工作二、手动为通道加入排序节点2.1. 生成加密材料与启动容器2.1.1. 为新增排序节点生成加密材料2.1.2. 启动新增的排序节点 2.2. 加入排序节点2.2.1. 获取最新的配置区块2.2.2. 将新增的排序节点加入到通道中 2.3. 通道配置更新2.3.1. 将通道配置转换为JSON格…

IoTDB 集群重启某节点失败

问题现象 IoTDB 1.3.3.6 版本部署的 3C3D 集群&#xff0c;在重启某个节点服务时失败&#xff0c;报错信息为节点冲突&#xff0c;日志部分截图如下&#xff1a; 问题原因 当前 IoTDB 会根据 data/confignode/system 路径下的 confignode-system.properties 文件及 data/data…

【EXCEL】【VBA】处理GI Log获得Surf格式的CONTOUR DATA

【EXCEL】【VBA】处理GI Log获得Surf格式的CONTOUR DATA data source1: BH coordination tabledata source2:BH layer tableprocess 1:Collect BH List To Layer Tableprocess 2:match Reduced Level from "Layer"+"BH"data source1: BH coordination…