整型变量的原子操作

什么是原子操作

原子操作(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 替换为 10
    if (success) {
        std::cout << "Exchange successful" << std::endl;
    } else {
        std::cout << "Exchange failed" << std::endl;
    }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Git如何清除账户凭证

场景&#xff1a;一般发生在Git用户变更的情况 1.git base 操作 Git会使用凭证助手 credential.helper来储存账户凭证&#xff0c;通过以下命令移除&#xff1a; git config --system --unset credential.helper 除了system系统级外&#xff0c;还有 global、local范围。 查…

直方图均衡化原理和实现

基本思想 将原始图像的直方图分布转换为一个均匀分布的直方图&#xff0c;这样原图中的高频率亮度值会被展宽&#xff0c;而低频率亮度值则被压缩&#xff0c;从而达到增强图像对比度的效果。 计算过程 假设我们有一个灰度图像&#xff0c;其像素值范围从0到L-1&#xff08;…

云计算 3月11号 (NFS远程共享存储及vsftpd配置)

构建NFS远程共享存储 一、NFS介绍 文件系统级别共享&#xff08;是NAS存储&#xff09; --------- 已经做好了格式化&#xff0c;可以直接用。 速度慢比如&#xff1a;nfs&#xff0c;sambaNFS NFS&#xff1a;Network File System 网络文件系统&#xff0c;NFS 和其他文件…

关于使用elementUI中select和el-checkbox-group的回显问题

网上看到很关于这个的问题回显&#xff0c;各式各样&#xff0c;没有绝句自己的问题&#xff0c;总重问题出在数据格式上 select和el-checkbox-group el-checkbox 都是字符串数组格式&#xff1a;[12,13,....]; 我写的格式是id是选中的id组成的回显数据格式&#xff1b; 如…

Flutter 多语言自动化本地化生成器

Flutter 多语言自动化本地化生成器 这是一个为Flutter设计的插件&#xff0c;通过从Excel表格提取的CSV文件自动生成Dart本地化文件&#xff0c;以简化应用程序本地化的流程。这个工具通过自动化创建多语言资源文件&#xff0c;简化了开发人员和翻译人员的工作流程。 特点 默…

Android 录屏操作

Android 录屏操作 本文主要介绍android中如何通过MediaRecorder实现录屏操作的. 1: 申请权限 <uses-permission android:name"android.permission.RECORD_AUDIO" /> <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE"…

react native常用插件

react-native-async-storage/async-storage 说明&#xff1a;AsyncStorage 是一个在 react-native 中轻量存储的库&#xff1b;跟 localStorage 类似&#xff0c;API 也几乎一样&#xff1b;存储的时候需要将存储内容转成字符串存储。 react-navigation/material-bottom-tabs …

Python Web开发记录 Day9:Django part3 用户管理

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1、数据库准备2、用户列表3、新建用户4、编辑用…

基于YOLOv8深度学习的脑肿瘤智能检测系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测、智慧医疗

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

发布一个npm包到 Nexus私有仓库

前文&#xff1a;使用nexus3搭建npm私有仓库 1、前置条件 git、 nvm、nrm、monorepo 的概念&#xff0c;以及 lerna 的使用、 yarn 的使用 基于 lerna yarn 的 monorepo 仓库 lerna npm i -g lernamac : zsh: command not found: lerna brew install lerna2、添加nexus权…

IDEA中配置Tomcat

文章目录 一、创建Web项目二、配置tomcat三、启动Tomcat 一、创建Web项目 1.首先我们要用IDEA创建一个普通的java项目。 2.我创建的项目名称为myTomcat&#xff0c;想要开发web程序&#xff0c;我们还要做一下操作。 首先我们先给项目添加框架支持...&#xff0c;我的 idea 版…

ASP.NET

Web控件 Web控件-内部控件 ASP.NET引入一组称为”内部控件”的新控件&#xff0c;它们专门用于ASP.NET 内部控件的使用方法与HTML控件相同&#xff0c;它们映射到HTML元素并通过使用 runat”server”属性在服务器上执行 Web控件-列表控件 这些控件用于在Web页中创建数据列表…

uniapp移动端 IOS系统下无法与webview通信

不知道有没有人遇到过这个问题 我的页面嵌套了一个webview&#xff08;文件位于项目的hybrif/html&#xff09;目录下 使用evalJS与webview进行通信 代码如下 在安卓里运行是没问题的&#xff0c;但在苹果手机上一直无法通信 连接真机&#xff0c;打印evalJS是个方法&#xf…

【matlab】如何将.mat文件与.nii文件互转

【matlab】如何将.mat文件与.nii文件互转 .mat转为.nii文件 有时候代码需要读取的是.nii文件&#xff0c;但是如何现有的数据是.mat格式&#xff0c;需要将.mata转化为.nii文件 1、先加载.mat文件 % 加载.mat文件 load(your_mat_file.mat); % 请将your_mat_file.mat替换为实…

中创ET4410台式电桥固件升级工具(修复了列表扫描的BUG)

中创ET4410台式LCR数字电桥固件升级工具和最新版固件&#xff08;修复了列表扫描的BUG&#xff09; 中创ET4410 台式LCR数字电桥 简单开箱测评&#xff1a;https://blog.zeruns.tech/archives/763.html 之前买的中创ET4410台式LCR数字电桥固件有BUG&#xff08;胜利的VC4090C…

鸿蒙 Harmony 初体验

前言 看现在网上传得沸沸扬扬的鸿蒙&#xff0c;打算弄个 hello world 玩一下, 不然就跟不上时代的发展了 环境安装 我的环境 Windows 11 家庭中文版HarmonyOS SDK (API 9)DevEco Studio (3.1.1 Release)Node.js (16.19.1) 开发IDE下载 官方下载链接 配置 nodejs 这里帮…

command failed: npm install --loglevel error --legacy-peer-deps

在使用vue create xxx创建vue3项目的时候报错。 解决方法&#xff0c;之前使用的https://registry.npm.taobao.org 证书过期更换镜像地址即可 操作如下&#xff1a; 1.cd &#xff5e;2.执行rm .npmrc3. sudo npm install -g cnpm --registryhttp://registry.npmmirror.com…

排查数据库插入慢的问题

文章目录 项目背景问题排查思路排查网络问题检查两台服务器之间的网络是否通畅检查两台服务器之间的网速iperf3 下载地址iperf3 使用方法 排查数据库锁表检查数据库性能 项目背景 我负责的模块是这个应用的一部分&#xff08;在服务器A&#xff09;&#xff0c;但数据库&#…

分治算法和树

一&#xff1a;分治算法 「 divide and conquer」&#xff0c;全称分而治之&#xff0c;是一种非常重要且常见的算法策略。 分治通常基于递归实现&#xff0c;包括“分”和“治”两个步骤。 分&#xff08;划分阶段&#xff09;&#xff1a;递归地将原问题分解为两个或多个子…

Ansible自动化运维Inventory与Ad-Hoc

前言 自动化运维是指利用自动化工具和技术来简化、自动化和优化IT基础设施的管理和运维过程&#xff0c;从而提高效率、降低成本&#xff0c;并减少人为错误。在当今复杂的IT环境中&#xff0c;自动化运维已经成为许多组织和企业提高生产力和保证系统稳定性的重要手段。Ansibl…