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…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...

GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...