整型变量的原子操作

什么是原子操作

原子操作(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.rhkb.cn/news/276450.html

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

相关文章

H5 流光分割个人主页源码

源码名称&#xff1a;流光分割个人主页源码 源码介绍&#xff1a;一款流光分割特效个人主页源码&#xff0c;源码带大量跳转个人联系方式按钮和朋友按钮。同时带有个人介绍。 需求环境&#xff1a;H5 下载地址&#xff1a; https://www.changyouzuhao.cn/10241.html

稀碎从零算法笔记Day15-LeetCode:判断子序列

跑样例的时候LC炸了&#xff0c;以为今天回断更 题型&#xff1a;字符串、双指针 链接&#xff1a;392. 判断子序列 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述&#xff08;此题建议结合样例理解&#xff09; 给定字符串 s 和 t &#xf…

妇女节专访|勇敢踏入未知领域,她的 Web3 奇妙之旅

Web3 的出现席卷着数字世界的剧烈变革&#xff0c;让每个人与互联网和数字资产互动的方式产生了深刻的变化。Web3 所强调的去中心化特征&#xff0c;使其成为人们对理想未来世界的一个缩影。而作为一个以技术为核心的新兴领域&#xff0c;Web3 也难以避免传统认知中男性占主导地…

Unity 让角色动起来(动画控制器)

下载素材&#xff1a; 导入后&#xff0c;找到预制体和动画。 新建动画控制器&#xff0c;拖动到预制体的新版动画组件上。 建立动画关系 创建脚本&#xff0c;挂载到预制体上。 using System.Collections; using System.Collections.Generic; using UnityEngine;public c…

C++面试100问!(三)

前言 本次专题旨在回顾C的一些基础&#xff0c;方便实时总结。 C源文件从文本到可执行文件经历的过程&#xff1f; 预处理阶段&#xff1a;对源代码文件中文件包含关系&#xff08;头文件&#xff09;、预编译语句&#xff08;宏定义&#xff09;进行分析和替换&#xff0c;生…

基于Java+SpringBoot+vue实现图书借阅和销售商城一体化系统

基于JavaSpringBootvue实现图书借阅和销售商城一体化系统 &#x1f345; 作者主页 央顺技术团队 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留…

【阿里云系列】-部署ACK集群的POD应用日志如何集成到日志服务(SLS)中

介绍 我们在实际部署应用到阿里云的ACK集群后&#xff0c;由于后期应用服务的持续维护诉求可能需要跟踪排查问题&#xff0c;此时就要具备将应用的历史日志存档便于后期排查问题 处理方式 为了解决以上的普遍需求&#xff0c;需要将ACK中的应用日志采集到SLS的Logstore中,然…

unity显示当前时间

1建立文本组件和一个空对象 2创建一个脚本并复制下面代码 using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine;public class showtime: MonoBehaviour {public TextMeshProUGUI time;private void Update(){string currentTime Sy…

VMware workstation的安装

VMware workstation安装&#xff1a; 1.双击VMware-workstation-full-9.0.0-812388.exe 2.点击next进行安装 选择安装方式 Typical&#xff1a;典型安装 Custom&#xff1a;自定义安装 选择程序安装位置 点击change选择程序安装位置&#xff0c;然后点击next 选择是否自动…

Linux下的多线程编程:原理、工具及应用(1)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;Flower of Life—陽花 0:34━━━━━━️&#x1f49f;──────── 4:46 &#x1f504; ◀️ ⏸ ▶️ ☰ …

【项目设计】基于Httplib和Mysql的视频播放

项目源码&#xff08;绝对可以直接运行&#xff09; 一、项目介绍 1. 对视频播放系统的认识 搭建视频共享播放服务器&#xff0c;可以让所有人通过浏览器访问服务器&#xff0c;实现视频的上传查看&#xff0c;以及管理并播放的功能。主要是完成服务器端的程序业务功能的实现…

vue 引用百度地图

address.vue <template><div><!-- 地图 --><el-drawer:visible.sync"type1"direction"rtl"size"50%"append-to-bodyclass"map-drawer":before-close"beforeClose"><div style"width: 100%…

永热爱 敢向前 | Paraverse平行云的2023 年终总结

永热爱&#xff0c;敢向前 值此新年&#xff0c;回顾2023&#xff0c;仅以此句&#xff0c;献给所有XR产业信仰者 2023 年&#xff0c;是XR产业技术和场景承上启下的关键之年 在这场波澜壮阔的技术潮中 「Paraverse平行云」踏浪前行 已是第八个年头&#xff0c;让我们一起…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的远距离停车位检测系统(深度学习代码+UI界面+训练数据集)

摘要&#xff1a;开发远距离停车位检测系统对于提高停车效率具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个远距离停车位检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#xff0c;展示了不…

2024 MCM数学建模美赛2024年A题复盘,思路与经验分享:资源可用性与性别比例 | 七鳃鳗的性别比例变化对生态系统稳定性的影响(四)

审题 第三问要我们评估七鳃鳗的性别比例变化对生态系统稳定性的影响。 这里我们就要去查一下生态系统稳定性的定义。 通过查资料我们知道&#xff0c;生态系统稳定性包括生态系统的抵抗力和恢复力。 OK&#xff0c;到这里问题就变成了&#xff0c;七鳃鳗的性别比例对生态系…

LeetCode2115. 从给定原材料中找到所有可以做出的菜

拓扑排序 题面 题目链接&#xff1a;2115. 从给定原材料中找到所有可以做出的菜 - 力扣&#xff08;LeetCode&#xff09; 你有 n 道不同菜的信息。给你一个字符串数组 recipes 和一个二维字符串数组 ingredients 。第 i 道菜的名字为 recipes[i] &#xff0c;如果你有它 所有…

游戏免费下载平台模板源码

功能介绍 此游戏网站模板源码是专门为游戏下载站而设计的&#xff0c;旨在为网站开发者提供一个高效、易于维护和扩展的解决方案。 特点&#xff1a; 响应式设计&#xff1a;我们的模板可以自适应不同设备屏幕大小&#xff0c;从而为不同平台的用户提供最佳的浏览体验。 …

Qt中进行客户端开发框架

在Qt中进行客户端开发是一种常见的做法&#xff0c;Qt是一个跨平台的C框架&#xff0c;提供了丰富的工具和类库&#xff0c;用于开发图形用户界面&#xff08;GUI&#xff09;应用程序、网络应用程序以及其他类型的软件。以下是一些常用的Qt客户端开发框架和技术&#xff0c;希…

php.exe运行时,提示缺少VCRUNTIME140.dll

php.exe运行时&#xff0c;提示缺少VCRUNTIME140.dll 下载地址 https://www.microsoft.com/zh-cn/download/details.aspx?id48145根据需要选择下载3.运行安装后&#xff0c;再次运行php.exe。

已解决:android SDK安装时点击SDK Manager出现闪退

1、首先确保电脑里边安装了JDK&#xff0c;并且要把安装路径配置在环境变量里边&#xff0c;避免使用绝对路径 推荐%JAVA_HOME%\bin 2、在C:\Users\huanhuan\Desktop\android-sdk-windows\tools路径下找到android.bat文件打开&#xff0c;把set java_exe后改为jdk中java.exe的路…