整型变量的原子操作

news2024/11/16 15:33:58

什么是原子操作

原子操作(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;
}

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

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

相关文章

根据服务器系统选择对应的MySQL版本

1. 根据服务器系统选择对应的MySQL版本 MySQL有多个版本&#xff0c;选择对应的版本&#xff0c;重点信息是Linux的GLIBC版本号&#xff0c;Linux的版本、系统位数。 1.1 查看Linux的GLIBC版本号 通常libc.so会支持多个版本&#xff0c;即向前兼容&#xff0c;查看该文件中…

管理类联考--复试--英文面试--经典问题

文章目录 考研复试英语口试日常问题及连续表达问题一、考研复试英语口试的相关事项二、考研英语复试口语日常问题列举考研原因 (reasons for my choice)研究生期间你的计划 (plans in the postgraduate study)介绍你的家乡 (hometown)你的家庭 (family)你的大学 (university)研…

【AI论文阅读笔记】ResNet残差网络

论文地址&#xff1a;https://arxiv.org/abs/1512.03385 摘要 重新定义了网络的学习方式 让网络直接学习输入信息与输出信息的差异(即残差) 比赛第一名1 介绍 不同级别的特征可以通过网络堆叠的方式来进行丰富 梯度爆炸、梯度消失解决办法&#xff1a;1.网络参数的初始标准化…

固态硬盘有缓存和没缓存有什么区别

固态硬盘&#xff08;SSD&#xff09;已经成为现代计算机的重要组成部分&#xff0c;它们提供了比传统机械硬盘更快的读写速度&#xff0c;从而显著提升了操作系统的运行速度和应用程序的加载效率。 其中&#xff0c;缓存&#xff08;Cache&#xff09;是固态硬盘中一个重要的…

Ansible inventory文件详解

我们知道inventory主要用来管理managed nodes(host),定义playbook要在哪些远程机器上执行。如果不熟悉inventory的角色的,可以参考ansible playbook基本概念。 在具体讲解如何构建inventory文件之前,我们必须知道在ansible的配置文件中,有一个参数inventory用于指定默认的…

PyQt4应用程序的PDF查看器

最近因为项目需要创建一个基于PyQt4的PDF查看器应用程序&#xff0c;正常来说&#xff0c;我们可以使用PyQt4的QtWebKit模块来显示PDF文件。那么具体怎么实现呢 &#xff1f;以下就是我写的一个简单的示例代码&#xff0c;演示如何创建一个PyQt4应用程序的PDF查看器&#xff1a…

几何相互作用GNN预测3D-PLA

预测PLA是药物发现中的核心问题。最近的进展显示了将ML应用于PLA预测的巨大潜力。然而,它们大多忽略了复合物的3D结构和蛋白质与配体之间的物理相互作用,而这对于理解结合机制至关重要。作者提出了一种结合3D结构和物理相互作用的几何相互作用图神经网络GIGN,用于预测蛋白质…

每周一算法:迭代加深搜索

题目链接 加成序列 题目描述 满足如下条件的序列 X X X&#xff08;序列中元素被标号为 1 、 2 、 3 … m 1、2、3…m 1、2、3…m&#xff09;被称为加成序列&#xff1a; X [ 1 ] 1 X[1]1 X[1]1 X [ m ] n X[m]n X[m]n X [ 1 ] < X [ 2 ] < … < X [ m − 1 ]…

泽众云真机-机型支持ADB调试功能即将上线

最近云真机平台在线客服&#xff0c;收到很多咨询关于ADB调试功能&#xff0c;什么时候能更新&#xff1f;据小编所知&#xff0c;正在升级之中&#xff0c;有一块专门为了解决ADB调试功能提前准备&#xff0c;升级网络硬件设备&#xff0c;目前平台的功能已开发完成&#xff0…

windows 资源管理器(explorer)占用高的问题

找到配置 win r 输入 gpedit.msc 打开本地组策略管理器 扎到本地计算机策略-》 用户配置-》管理模板-》 windows组件-》 搜索 将后续的设置项均设置为已禁用

SpringTask实现的任务调度与XXL-job实现的分布式任务调度【XXL-Job工作原理】

目录 任务调度 分布式任务调度 分布式任务调度存在的问题以及解决方案 使用SpringTask实现单体服务的任务调度 XXL-job分布式任务调度系统工作原理 XXL-job系统组成 XXL-job工作原理 使用XXL-job实现分布式任务调度 配置调度中心XXL-job 登录调度中心创建执行器和任务 …

CVPR2023 | 3D Data Augmentation for Driving Scenes on Camera

3D Data Augmentation for Driving Scenes on Camera 摄像机驾驶场景的 3D 数据增强 摘要翻译 驾驶场景极其多样和复杂&#xff0c;仅靠人力不可能收集到所有情况。虽然数据扩增是丰富训练数据的有效技术&#xff0c;但自动驾驶应用中现有的摄像头数据扩增方法仅限于二维图像…

MySQL order by 语句执行流程

全字段排序 假设这个表的部分定义是这样的&#xff1a; CREATE TABLE t (id int(11) NOT NULL,city varchar(16) NOT NULL,name varchar(16) NOT NULL,age int(11) NOT NULL,addr varchar(128) DEFAULT NULL,PRIMARY KEY (id),KEY city (city) ) ENGINEInnoDB; 有如下 SQL 语…

自动控制原理--matlab/simulink建模与仿真

第一讲 自动控制引论 第二讲 线性系统的数学模型 第三讲 控制系统的复域数学模型(传递函数) 第四讲 控制系统的方框图 /video/BV1L7411a7uL/?p35&spm_id_frompageDriver pandas, csv数据处理 numpy&#xff0c;多维数组的处理 Tensor&#xff0c;PyTorch张量 工作原理图…

留学生课设|R语言|研究方法课设

目录 INSTRUCTIONS Question 1. Understanding Quantitative Research Question 2. Inputting data into Jamovi and creating variables (using the dataset) Question 3. Outliers Question 4. Tests for mean difference Question 5. Correlation Analysis INSTRUCTIO…

Tomcat的使用

1. Tomcat 1.1 Tomcat 是什么 Tomcat 就是基于 Java 实现的一个开源免费, 也是被广泛使用的 HTTP 服务器 1.2 下载安装 Tomcat官网选择其中的 zip 压缩包, 下载后解压缩即可&#xff0c;解压缩的目录最好不要带 “中文” 或者 特殊符号 进入 webapps 目录,每个文件夹都对应…

graylog API 弱密码

graylog web 页面密码设置 输入密码&#xff1a;获取sha256加密后密码 echo -n "Enter Password: " && head -1 </dev/stdin | tr -d \n | sha256sum | cut -d" " -f1vi /etc/graylog/server/server.conf #修改以下配置 root_usernameroot ro…

Monorepo 解决方案 — 基于 Bazel 的 Xcode 性能优化实践

背景介绍 书接上回《Monorepo 解决方案 — Bazel 在头条 iOS 的实践》&#xff0c;在头条工程切换至 Bazel 构建系统后&#xff0c;为了支持用户使用 Xcode 开发的习惯&#xff0c;我们使用了开源项目 Tulsi 作为生成工具&#xff0c;用于将 Bazel 工程转换为 Xcode 工程。但是…

【爬虫开发】爬虫从0到1全知识md笔记第1篇:爬虫概述【附代码文档】

爬虫开发从0到1全知识教程完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;爬虫概述。selenium的其它使用方法。Selenium课程概要。常见的反爬手段和解决思路。验证码处理。chrome浏览器使用方法介绍。JS的解析。Mongodb的介绍和安装,小结。mongodb的简单使…

信息系统项目管理师--干系人管理

干系人会受到项⽬积极或消极的影响&#xff0c;或者能对项⽬施加积极或消极的影响 项⽬经理和团队管理⼲系⼈的能⼒决定着项⽬的成败。为提⾼项⽬成功的概率&#xff0c; 尽早开始识别⼲系⼈并引导⼲系⼈参与。当项⽬章程被批准、项⽬经理被委任&#xff0c;以及团队开始组建之…