整型变量的原子操作

什么是原子操作

原子操作(Atomic Operation)是指不可中断的操作,即在多线程环境下,当一个线程在执行原子操作时,不会被其他线程的调度和中断所影响。这种操作在多线程编程中尤为重要,因为它能保证操作的原子性,从而避免数据竞争和不一致。

原子操作的特性

  1. 原子性:操作不可分割,即不可中断。
  2. 可见性:操作完成后,其他线程能立即看到结果。
  3. 有序性:编译器和处理器不会重排序原子操作。

c++ 原子操作的支持

在C++中,原子操作可以通过<atomic>库来实现。<atomic>库提供了一组模板类,如std::atomic<T>,其中T可以是整型、指针类型等。这些模板类提供了一系列成员函数,如load()store()exchange()compare_exchange_weak()compare_exchange_strong()等,以实现原子操作。

示例使用不适用原子操作和使用原子操作比对

示例我们创建二十个线程,同时分别对同一个对象的成员变量(m_aa(初值为0))做10000自增运算,按照正常运算,所有线程运行完成后,对象的成员变量值应该是20*10000 = 200000.

不使用原子操作

#include<iostream>
#include<thread>
#include<vector>
#include<atomic>
class ThreadTsetAtomic
{
private:int m_aa;// std::atomic<int> m_aa;
public:void add(){   int b=10000;while(b--){m_aa++;}}ThreadTsetAtomic(const int a):m_aa(a){}void showValue(){std::cout<<m_aa<<std::endl;}
};void testFun(ThreadTsetAtomic* sub)
{sub->add();
}
int main()
{   ThreadTsetAtomic test1(0);std::vector<std::thread> threadVec;for(int i=0;i<20;i++){   std::thread test(testFun,&test1);threadVec.push_back(std::move(test));// threadVec.emplace_back(testFun,test1);}for (auto& t : threadVec) {  t.join();  }  test1.showValue();return 0;
}

编译运行

可以看到这里运行的结果是30799 和我们实际预期的200000值相差很大

使用原子操作

示例

#include<iostream>
#include<thread>
#include<vector>
#include<atomic>
class ThreadTsetAtomic
{
private:// int m_aa;std::atomic<int> m_aa; //原子整型变量m_aa
public:void add(){   int b=10000;while(b--){m_aa++;}}ThreadTsetAtomic(const int a):m_aa(a){}void showValue(){std::cout<<m_aa<<std::endl;}
};void testFun(ThreadTsetAtomic* sub)
{sub->add();
}
int main()
{   ThreadTsetAtomic test1(0);std::vector<std::thread> threadVec;for(int i=0;i<20;i++){   std::thread test(testFun,&test1);threadVec.push_back(std::move(test));// threadVec.emplace_back(testFun,test1);}for (auto& t : threadVec) {  t.join();  }  test1.showValue();return 0;
}

编译运行

运行结果为200000和我们预期的结果一致

为什么示例一中不使用原子操作运行的结果和我们预期的值相差这么大呢?

示例

#include<iostream>int main()
{int a =0;a++;return 0;
}

编译运行

我们查看想加的汇编代码

0x0000555555554745 <+11>:    addl   $0x1,-0x4(%rbp) 这一句的实现

  1. 地址计算: 首先,CPU 将寄存器 %rbp 的值与偏移量 -0x4 相加,得到内存地址 -0x4(%rbp)

  2. 内存访问: CPU 访问计算得到的内存地址,读取其中的值。这个值是存储在该内存位置中的数据,可能是一个整数值。

  3. 加法操作: CPU 将从内存中读取的值与立即数 0x1 相加,得到一个新的结果。

  4. 写回内存: 最后,CPU 将加法结果写回到内存地址 -0x4(%rbp) 所指向的内存位置中。这会覆盖原来的值,更新为新的结果。

也就是这个过程并非原子操作,因为涉及多个步骤,其中可能会发生中断、上下文切换或其他并发操作。要确保该操作是原子的,可能需要使用硬件支持的原子操作指令或锁来确保在多线程环境下的原子性。

结合上面示例不加原子操作分析,也就是多线程运行时,整形变量的自加不是原子操作的,当一个线程的操作还未完成可能这时候cpu就进行了线程切换,从而导致计数值不准。

补充

std::atomic  API

  1. 加载(Load)和存储(Store):

  • T load(std::memory_order order = std::memory_order_seq_cst) const noexcept;
  • void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept;

这对函数允许加载和存储原子变量的值。load 函数会返回当前原子变量的值,而 store 函数会将给定的值存储到原子变量中。

#include <atomic>
#include <iostream>std::atomic<int> value(0);int main() {value.store(10); // 存储值为 10 到原子变量int loaded_value = value.load(); // 加载原子变量的值std::cout << "Loaded value: " << loaded_value << std::endl;return 0;
}
  1. 交换(Exchange):

  • T exchange(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept;

这个函数会原子地将给定的值存储到原子变量中,并返回原子变量之前的值。

#include <atomic>
#include <iostream>std::atomic<int> value(0);int main() {int previous_value = value.exchange(10); // 原子地将值 10 存储到原子变量,并返回之前的值std::cout << "Previous value: " << previous_value << std::endl;return 0;
}
  1. 比较并交换(Compare and Exchange):

  • bool compare_exchange_weak(T& expected, T desired, std::memory_order success, std::memory_order failure) noexcept;
  • bool compare_exchange_strong(T& expected, T desired, std::memory_order success, std::memory_order failure) noexcept;

这对函数尝试原子地将原子变量的值与期望值进行比较,如果相等,则将新值存储到原子变量中,并返回 true;否则,返回 false。compare_exchange_weakcompare_exchange_strong 的区别在于当原子变量的值与期望值不同时,compare_exchange_weak 可能会失败,而 compare_exchange_strong 会循环直到操作成功。

#include <atomic>
#include <iostream>std::atomic<int> value(0);int main() {int expected = 0;int desired = 10;bool success = value.compare_exchange_weak(expected, desired); // 尝试将值从 0 替换为 10if (success) {std::cout << "Exchange successful" << std::endl;} else {std::cout << "Exchange failed" << std::endl;}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/746580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始搭建医保购药APP:技术选择与开发流程

医保购药APP作为一种创新的医疗服务工具&#xff0c;为用户提供了便捷的医保购药流程&#xff0c;同时也为医疗机构提供了更高效的管理和服务方式。今天小编将为大家讲解如何从零开始搭建一款医保购药APP&#xff0c;包括技术选择和开发流程。 一、技术选择 在搭建医保购药APP…

openssl3.2 - note - Decoders and Encoders with OpenSSL

文章目录 openssl3.2 - note - Decoders and Encoders with OpenSSL概述笔记编码器/解码器的调用链OSSL_STORE 编码器/解码器的名称和属性OSSL_FUNC_decoder_freectx_fnOSSL_FUNC_encoder_encode_fn官方文档END openssl3.2 - note - Decoders and Encoders with OpenSSL 概述 …

js-循环返回多条数据,每条数据文本超过三行进行展开与收起的实现

1.基本需求 返回多条数据&#xff0c;每条数据在一定宽度的盒子内&#xff0c;文本超过三行进行文本的展开与收起 2.实现逻辑 对于返回的每条数据添加属性expend&#xff1a;false来在循环中进行对于展开收起的判断。 动态计算盒子宽度随着分辨率的变化而变化的值boxWidth。获…

基于单片机的智能环境监测系统

摘 要 本设计主要由温湿度检测、烟雾检测电路、报警电路、显示电路和人体检测等模块组成。温湿度检测部分使用的是DHT11这种型号的温湿度传感器。DHT11是一种单总线型数字式温湿度传感器&#xff0c;它具有误差小、分辨率高、抗干扰能力强等特点。在烟雾检测电路模块当中&…

Nginx 隐藏版本号

只是隐藏 Nginx 版本号&#xff0c;只需在 Nginx 配置文件全局段添加 server_tokens off 然后重启 Nginx 服务 默认安装好了的 Nginx 服务 查看版本 现在修改配置 重启 Nginx 版本信息看不到了 我们下期见&#xff0c;拜拜&#xff01;

【MySQL】LEFT JOIN表时条件的位置对查询结果的影响

前言 我们知道,JOIN表的查询条件可以放在ON语句处,也可以放在 WHERE 语句处。 用 t2.status = 1 这条件举例: 方式1:放在 WHERE 语句处 SELECT t1.* FROM table1 t1 LEFT JOIN table2 AS t2 ON t2.k1 = t1.k2 WHERE t2.`status` = 1方式2:放在 ON 语句处 SELECT t1.*…

vsto判断workbook是否正在计算中

您可以使用 Workbook 对象的 CalculationState 属性来判断工作簿是否正在计算中。该属性返回一个 XlCalculation 枚举值&#xff0c;可以通过检查其是否等于 XlCalculation.xlCalculationManual 来确定工作簿是否处于手动计算模式&#xff0c;或者检查其是否等于 XlCalculation…

AIX7.2下安装python3

我们可以下载python3对应的AIX版本的安装包 http://www.aixtools.net/index.php/python3 直接执行 installp -d aixtools.python3.3.6.9.0.I -a all 安装完成后&#xff0c;默认安装位置在/opt/bin/python3

有趣之matlab-烟花

待整合1 2 3 动态 有趣编程之11 静态 逼真 3 .m文件路径下放back1.jpg back4.jpg…背景照片 点击screen 就会有小白点升起&#xff0c;爆炸 function yanhuamoban()clear all;%定义全局变量global ah ;%坐标轴句柄global styleNum ;%爆炸图案样式global multiColor; %多颜色变换…

Win11安装Plsql140报错2503

一、安装异常 二、解决办法 出现上述问题&#xff0c;主要是因为msi包安装的权限问题&#xff0c;使用管理员权限安装即解决 。cmd控制台以管理员身份打开WINR&#xff09;->(SHIFTCTRLRNTER)&#xff0c;进入到msi安装包目录下&#xff0c;以管理员身份安装即可&#xff1…

mysql数据库:使用 bash脚本 + 定时任务 自动备份数据

mysql数据库&#xff1a;使用 bash脚本 定时任务 自动备份数据 1、前言2、为什么需要自动化备份&#xff1f;3、编写备份脚本4、备份脚本授权5、添加定时任务6、重启 crond / 检查 crond 服务状态7、备份文件检查 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏…

Spring MVC共享域对象操作

1. 属性&#xff08;共享&#xff09;域介绍 在 JavaWeb 中&#xff0c;共享域指的是在 Servlet 中存储数据&#xff0c;以便在同一 Web 应用程序的多个组件中进行共享和访问。常见的共享域有四种&#xff1a;ServletContext、HttpSession、HttpServletRequest、PageContext。…

LeetCode28.找出字符串中第一个匹配项

28.找出字符串中第一个匹配项 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 示例 1&#xff1a; 输入…

C++中的C标准库、注释和条件编译

C中的C标准库、注释和条件编译 C标准库 C作为一种在C语言基础上发展起来的编程语言&#xff0c;保留了对C语言标准库的完整支持。这些C标准库提供了一系列的通用功能&#xff0c;如输入输出处理、字符串操作、数学计算等&#xff0c;允许C程序员利用已经熟悉的库来开展工作。…

虾皮Shopee根据ID取商品详情API

在构建电商平台的第三方应用或服务时&#xff0c;开发者经常需要通过编程方式获取商品的详细信息。对于虾皮Shopee平台来说&#xff0c;其提供的API接口使得这一需求得以实现。本文将详细介绍如何调用虾皮Shopee的API接口&#xff0c;根据商品ID获取其商品详情。 准备工作 在…

“优化消费环境 维护消费者权”荥阳市城关乡社工站开展3·15宣传志愿活动

又是一年“315”&#xff0c;为提高辖区群众消费维权意识&#xff0c;让他们可以学会运用政策法律知识维护自身合法权益&#xff0c;同时也为营造出辖区良好营商环境。2024年3月15日&#xff0c;在荥阳市民政局、荥阳市市场监督管理局、城关乡人民政府的支持下&#xff0c;城关…

“值此春意,共享蓝天”—携手植新绿·静待成荫时

种竹交加翠&#xff0c;栽桃烂漫红。——唐杜甫《春日江村》 春风十里&#xff0c;万物生长。春风吹拂绿色现&#xff0c;树吐嫩芽花鲜艳。植树正是好季节&#xff0c;男女老少齐挥铲。种下棵棵小树苗&#xff0c;辛勤培育勤浇灌。来年长成苍天木&#xff0c;绿树成荫美景见。 …

基于ElasticSearch存储海量AIS数据:时空立方体索引篇

文章目录 引言I 时间维切分II 空间范围切分引言 索引结构制约着查询请求的类型和处理方式,索引整体架构制约着查询请求的处理效率。随着时间推移,AIS数据在空间分布上具备局部聚集性,如 果简单地将所有AIS数据插入一个索引结构,随着数据量增长,索引的更新效率、查询效率及…

「字幕之美:解析硬、软、外挂,探寻视频世界的无声艺术」

硬字幕、软字幕与外挂字幕&#xff1a;概述 硬字幕、软字幕和外挂字幕是视频内容中常见的三种字幕形式&#xff0c;它们在提供文字信息的同时&#xff0c;为观众提供了更丰富的观看体验。下面将对这三种字幕进行概述&#xff1a; 硬字幕&#xff08;Hard Subtitles&#xff0…

RabbitMQ 模拟实现【六】:程序模拟实现

文章目录 模拟实现模拟消费者模拟生产者效果展示 启动结果如下&#xff1a; ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/71841546ad8043f1bd51e4408df791de.png)![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/f6e3e72ff9a4483c978ec48e24f075c2.p…