C++中的原子操作:原子性、内存顺序、性能优化与原子变量赋值
一、原子操作与原子性
原子操作(atomic operation)是并发编程中的一个核心概念,指的是在多线程环境中,一个操作一旦开始,就不会被其他线程的操作打断,直至该操作完成。这种不可分割的特性保证了操作的原子性,即要么全部做完,要么全部不做。原子操作在多线程编程中非常重要,因为它能有效避免数据竞争和条件竞争等问题,从而确保程序的正确性和稳定性。
C++11引入了原子操作,通过<atomic>头文件提供了一系列原子类型和函数,如std::atomic<T>,用于确保对共享数据的操作是原子的。原子类型提供了一系列成员函数来执行原子操作,这些操作包括加载(load)、存储(store)、加法(fetch_add、add)、减法(fetch_sub、sub)、交换(exchange)和比较并交换(compare_exchange_weak、compare_exchange_strong)等。
二、原子变量赋值操作
在C++中,原子变量赋值是通过std::atomic模板类实现的,提供了多种方法来对原子变量进行赋值和修改。以下是一些常见的原子变量赋值操作及其示例:
-
基本赋值
基本赋值操作是使用赋值运算符(=)直接将一个新值赋给原子变量。例如:
std::atomic<int> counter(0); // 声明一个原子整数变量,初始值为0
counter = 10; // 将10赋给counter
-
原子加法与减法
使用
fetch_add、add等成员函数可以实现原子加法操作,fetch_sub、sub等成员函数可以实现原子减法操作。fetch_add和fetch_sub返回加法或减法操作之前的值,而add和sub返回加法或减法操作之后的值。例如:
std::atomic<int> counter(0);int oldValue = counter.fetch_add(1); // 将counter的值加1,并返回加1之前的值int newValue = counter.add(5); // 将counter的值加5,并返回加5之后的值oldValue = counter.fetch_sub(3); // 将counter的值减3,并返回减3之前的值newValue = counter.sub(2); // 将counter的值减2,并返回减2之后的值
-
原子交换
使用
exchange成员函数可以实现原子交换操作,即将原子变量的当前值与一个新值进行交换,并返回交换之前的值。例如:
std::atomic<int> counter(5);
int oldValue = counter.exchange(10); // 将counter的值与10进行交换,并返回交换之前的值5
-
原子比较并交换
使用
compare_exchange_weak或compare_exchange_strong成员函数可以实现原子比较并交换操作。这两个函数都尝试将原子变量的当前值与一个期望值进行比较,如果相等,则将其设置为一个新值,并返回true;如果不相等,则返回false,并将期望值更新为当前值。compare_exchange_weak在某些平台上可能会由于性能优化而偶尔失败(即使当前值与期望值相等),而compare_exchange_strong则保证在当前值与期望值相等时一定会成功。例如:
std::atomic<int> counter(5);int expected = 5;bool success = counter.compare_exchange_strong(expected, 10); // 如果counter的值等于5,则将其设置为10,并返回true;否则返回false,并将expected更新为counter的当前值
- 原子性值传递
有时,我们需要将一个原子变量的值从一个对象复制到另一个对象。这可以通过load()和store()成员函数来实现。load()函数用于从原子变量中加载当前值,而store()函数用于将一个新值存储到原子变量中。以下是一个示例:
std::atomic<int> original(5); // 声明一个原子整数变量,初始值为5
std::atomic<int> target(0); // 声明另一个原子整数变量,初始值为0// 将original的值加载到局部变量中(虽然在这个例子中不是必需的,但展示了load的用法)
int value = original.load();// 直接将original的值存储到target中,这是一个原子操作
target.store(original.load()); // 将原始对象的值存储到目标对象// 此时,target的值也是5
三、内存顺序
内存顺序(Memory Order)是多线程编程中一个非常重要的概念,它定义了在多处理器或多核环境中,内存访问的次序。C++11标准明确引入了内存顺序,用于指定原子操作的顺序性,以避免多线程环境下的数据竞争问题。
C++11标准定义了多种内存顺序类型,包括memory_order_relaxed、memory_order_consume、memory_order_acquire、memory_order_release、memory_order_acq_rel和memory_order_seq_cst等。在实际编程中,开发者需要根据操作的目的和上下文环境来确定合适的内存顺序。
选择合适的内存顺序可以在保证正确性的前提下提高性能。例如,使用memory_order_relaxed可以放松对内存顺序的要求,从而减少同步开销,但可能会引入数据竞争的风险。相反,使用memory_order_seq_cst可以确保最强的顺序性保证,但可能会增加同步开销。
四、性能优化
原子操作通过避免锁的使用,减少了线程之间的竞争和上下文切换开销,从而提高了多线程程序的性能。然而,性能优化并非一味追求宽松的内存顺序,而需要在正确性和性能之间取得平衡。
以下是一些性能优化的建议:
-
选择合适的内存顺序:在保证线程安全的前提下,尽量使用宽松的内存顺序可以减少同步操作,从而提升性能。然而,过度放宽内存顺序可能会导致难以调试的并发问题。
-
利用硬件特性:不同CPU架构和编译器的实现对原子操作的支持和优化程度不同。深入理解平台特性,利用硬件提供的原子性支持和缓存一致性机制,可以进一步提高程序的性能。
-
减少不必要的同步:通过合理设计算法和数据结构,减少线程间的同步需求。例如,使用无锁数据结构、读写锁等高级同步机制,可以在保持线程安全的同时,减少同步开销。
-
避免忙等待:在需要等待某个条件成立时,避免使用忙等待(busy-waiting)的方式。忙等待会消耗大量的CPU资源,并可能导致性能下降。相反,可以使用条件变量、信号量等同步机制来实现高效的等待和通知机制。
综上所述,深入理解C++中的原子操作、原子性、内存顺序、性能优化以及原子变量赋值操作,对于编写高效且正确的并发代码至关重要。通过合理选择内存顺序、利用硬件特性、减少不必要的同步和避免忙等待等策略,可以在保证程序正确性的同时实现性能的优化。
相关文章:
C++中的原子操作:原子性、内存顺序、性能优化与原子变量赋值
一、原子操作与原子性 原子操作(atomic operation)是并发编程中的一个核心概念,指的是在多线程环境中,一个操作一旦开始,就不会被其他线程的操作打断,直至该操作完成。这种不可分割的特性保证了操作的原子…...
游戏引擎学习第19天
介绍 这段内容描述了开发者在进行游戏开发时,对于音频同步和平台层的理解和调整的过程。以下是更详细的复述: 开发者表达了他希望今天继续进行的工作内容。他提到,昨天他讲解了一些关于音频的内容,今天他想稍微深入讲解一下他正…...
RocketMQ: 专业术语以及相关问题解决
概述 要了解 RocketMQ 的多个关键特性的实现原理,并对消息中间件遇到的各种问题进行解决我们引用 JMS 规范 与 CORBA Notification 规范,规范为我们设计系统指明了方向但是仍有不少问题规范没有提及,对于消息中间件又至关重要RocketMQ 并不遵…...
C++ 类和对象中的 拷贝构造 和 运算符重载
构造函数中可以添加参数并添加默认值构成缺省构造,如果我们在构造函数的参数中加上自身类型类的引用和其他给出默认值的参数则会构成一种特殊的构造函数叫做———拷贝构造函数 1.拷贝构造 拷贝构造的特点: 1.拷贝构造函数是构造函数的一个重载 2.拷…...
el-table最大高度无法滚动
解决el-table同时使用fixed和计算的最大高度时固定右边的列无法跟随滚动的问题 原因:el-table组件会根据传入的 max-height 计算表格内容部分 和 fixed部分的最大高度,以此来生成滚动条和产生滚动效果,当传入的 max-height 为一个计算的高度…...
Vscode写markdown快速插入python代码
如图当我按下快捷键CRTLSHIFTK 自动出现python代码片段 配置方法shortcuts’ 打开这个json文件 输入 {"key": "ctrlshiftk","command": "editor.action.insertSnippet","when": "editorTextFocus","args&…...
基于 NCD 与优化函数结合的非线性优化 PID 控制
基于 NCD 与优化函数结合的非线性优化 PID 控制 1. 引言 NCD(Normalized Coprime Factorization Distance)优化是一种用于非线性系统的先进控制方法。通过将 NCD 指标与优化算法结合,可以在动态调整控制参数的同时优化控制器性能。此方法特别…...
【数据分析】基于GEE实现大津算法提取洞庭湖流域水体
大津算法提取水体 1.写在前面2.洞庭湖水体识别1.写在前面 最大类间方差法,也称为Otsu或大津法,是一种高效的图像二值化算法,由日本学者Otsu于1979年提出。该算法基于图像的频率分布直方图,假设图像包含两类像素(前景和背景),并计算出一个最佳阈值,以最大化类间方差,从…...
计算机网络安全 —— 报文摘要算法 MD5
一、报文摘要算法基本概念 使用加密通常可达到报文鉴别的目的,因为伪造的报文解密后一般不能得到可理解的内容。但简单采用这种方法,计算机很难自动识别报文是否被篡改。另外,对于不需要保密而只需要报文鉴别的网络应用,对整个…...
LeetCode 746. 使用最小花费爬楼梯 java题解
https://leetcode.cn/problems/min-cost-climbing-stairs/description/ 优化:可以不用dp数组,用变量,节省空间。 class Solution {public int minCostClimbingStairs(int[] cost) {int lencost.length;int[] dpnew int[len1];dp[0]0;//爬到0…...
Kubernetes的pod控制器
文章目录 一,什么是pod控制器二,pod控制器类型(重点)1.ReplicaSet2.Deployment3.DaemonSet4.StatefulSet5.Job6.Cronjob 三,pod与控制器的关系1.Deployment2.SatefulSet2.1StatefulSet组成2.2headless的由来2.3有状态服…...
ArcMap 处理栅格数据地形图配准操作
ArcMap 处理栅格数据地形图配准操作今天分享 一、地形图配准 1、绘图 点击 开始绘制,四条线 2、地理配准 1)点击弹出 2)画控制点 关闭自动校正 画线 从焦点向外划线,然后邮件输入坐标弹出框,填写相应内容,…...
comprehension
1.读题---猜---文章主题 只读题目,不读选项 2.文章--定位 3.用文章对应选项 1 be based on be dependent upon 2 fruitful adj.富有成效的;硕果累累的; 3 unfruitful adj.徒然的,无益的,没有结果的 4 desperately adv.拼命地&#x…...
开源宝藏:Smart-Admin 重复提交防护的 AOP 切面实现详解
首先,说下重复提交问题,基本上解决方案,核心都是根据URL、参数、token等,有一个唯一值检验是否重复提交。 而下面这个是根据用户id,唯一值进行判定,使用两种缓存方式,redis和caffeineÿ…...
使用 npm 安装 Electron 作为开发依赖
好的,下面是一个使用 npm pack 和 npm install 命令来打包和安装离线版本的 npm 包的具体示例。我们将以 electron 为例,演示如何在有网络连接的机器上打包 electron,然后在没有网络连接的机器上安装它。 步骤 1: 在有网络连接的机器上打包 …...
JavaWeb之综合案例
前言 这一节讲一个案例 1. 环境搭建 然后就是把这些数据全部用到sql语句中执行 2.查询所有-后台&前台 我们先写后台代码 2.1 后台 2.2 Dao BrandMapper: 注意因为数据库里面的名称是下划线分割的,我们类里面是驼峰的,所以要映射 …...
MySQL 报错:1137 - Can‘t reopen table
MySQL 报错:1137 - Can’t reopen table 1. 问题 对临时表查询: select a.ts_code,a.tsnum,b.tsnum from (select t.ts_code ,count(*) tsnum from tmp_table t group by t.ts_code having count(*) > 20 and count(*)< 50 ) a ,(select t.ts_…...
Claude3.5-Sonnet和GPT-4o怎么选(附使用链接)
随着人工智能模型的不断进化,传统的评估标准已经逐渐变得陈旧和不再适用。以经典的“喝水测试”为例,过去广泛应用于检测模型能力,但现如今即便是国内的一些先进模型,也能够轻松答对这些简单的问题。因此,我们亟需引入…...
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
在网上找了很多种办法 都解决不了; 最后发现是文本域字体设置出了问题; 在这不展示其他的代码 只展示重要代码; 1 引入扩展包 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</v…...
java-贪心算法
1. 霍夫曼编码(Huffman Coding) 描述: 霍夫曼编码是一种使用变长编码表对数据进行编码的算法,由David A. Huffman在1952年发明。它是一种贪心算法,用于数据压缩。霍夫曼编码通过构建一个二叉树(霍夫曼树&a…...
别再只盯着Ra了!从轴承到晶圆,聊聊三维粗糙度Sa怎么测更准
从Ra到Sa:三维粗糙度测量的技术革命与实操指南 在精密制造领域,表面粗糙度测量正经历一场静默但深刻的范式转移。当半导体工艺迈入5纳米时代,当轴承寿命要求突破百万转大关,传统二维线扫描的Ra参数越来越难以捕捉微观形貌的全貌。…...
告别C盘爆满!手把手教你将VS2010旗舰版安装到其他盘(附完整配置流程)
告别C盘爆满!手把手教你将VS2010旗舰版安装到其他盘(附完整配置流程) 对于开发者而言,Visual Studio 2010(VS2010)作为经典的开发环境,至今仍被许多项目所依赖。然而,随着系统盘空间…...
【RK3588-AI-004】RK3588 AI专属依赖环境预装(Python、OpenCV、基础编译工具)
📖 专栏介绍 本专栏为RK3588 端侧AI开发零基础实战教程,专为嵌入式AI入门、模型部署、视觉开发学习者打造。全程实操、无废话、避坑优化,从零搭建RK3588专属AI开发环境,手把手教学,新手也能轻松上手。 ✅ 硬件适配&am…...
小学期第一周
理论部分:学会了低通滤波器原理:只允许低于截止频率的信号通过,高于截止频率的信号被大幅衰减方波变成正弦波的原理:方波是基波无数奇次谐波的叠加,低通滤波器只留基波、滤掉高频谐波,输出就接近正弦波二阶…...
RAG 和 NotebookLM 都试过后,我才发现数据库知识库真正缺的不是搜索
很多数据库知识库不好用,不是模型不会答,而是知识没有被整理成可调用、可校验、可维护的资产。 前面几篇一直在聊 DB Agent。 聊 Skill,聊记忆,聊告警风暴,聊编排,也聊到了系统画像、历史案例和当前证据。…...
2026 最新 Web 安全入门教程 零基础全面吃透 Web 攻防
“未知攻,焉知防”——真正的安全始于理解攻击者的思维 在日益数字化的世界中,Web安全工程师已成为企业防护体系的“数字盾牌”。本文将提供一条清晰的进阶路径,助你在2025年的网络安全领域脱颖而出。 一、认知篇:理解安全本质 …...
如何在Mayo中使用剪辑平面和爆炸视图:复杂装配体分析利器
如何在Mayo中使用剪辑平面和爆炸视图:复杂装配体分析利器 【免费下载链接】mayo 3D CAD viewer and converter based on Qt OpenCascade 项目地址: https://gitcode.com/gh_mirrors/ma/mayo Mayo是一款功能强大的开源3D CAD查看器和转换器,基于Q…...
遥测数据定义的生产级落地规范指南
在分布式架构与微服务体系中,将 Tracing(链路)、Metrics(指标)、Logs(日志)三种遥测数据有机构建为“三位一体” (3D Observability) 的可观测性网络,是保障系统高可用性的基石。 以…...
算法实例分析:使数组相等的最小开销
使数组相等的最小开销通过题意分析可知要让所有值相等,必然不需要超出数据的最大最小值,因此左右边界可以预先缩小范围。然后根据我们上面的分析不断缩小搜索边界范围。关于函数的计算,只要统计所有数据与的差值再乘上权重即可。最后注意&…...
树莓派5/4B新手开箱:用官方Raspberry Pi Imager工具10分钟完成系统部署
树莓派5/4B极速部署指南:官方Imager工具的全新工作流解析 第一次拿到树莓派5或4B时,很多用户会陷入传统部署方法的复杂流程中——下载镜像、格式化存储卡、烧录系统、手动配置网络……这些步骤不仅耗时,还容易因操作失误导致启动失败。而树莓…...
