整型变量的原子操作

什么是原子操作

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

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

相关文章

基于单片机的公交车IC卡操作系统的设计

目 录 摘 要 III Abstract IV 前 言 1 第一章 绪论 2 1.1 设计的背景和意义 2 1.2 设计的现状和发展 2 1.3 设计的目的与意义 2 第二章 总体设计 4 2.1 总体方案的设计与实现 4 2.1.1 主要设计的内容 4 2.1.2 系统的总体设计 4 2.2 系统方案论证 5 2.2.1 单片机的选择 6 2.2.2…

冒泡排序,详详解解

目录 基本概念&#xff1a; 上图&#xff1a; 核心思路&#xff1a; 基本步骤&#xff1a; 关键&#xff1a; 代码核心&#xff1a; 补充&#xff1a; 代码&#xff08;规范&#xff09; &#xff1a; 代码&#xff08;优化&#xff09;&#xff1a; 今天我们不刷力扣了&…

IP在网络通信中的重要作用

IP&#xff0c;全称Internet Protocol&#xff0c;即网际互连协议&#xff0c;是TCP/IP体系中的网络层协议。IP作为整个TCP/IP协议族的核心&#xff0c;是构成互联网的基础。它的作用重大且深远&#xff0c;下面将详细阐述IP的定义及其在网络通信中的重要作用。 首先&#xff0…

华为OD技术C卷“测试用例执行计划”Java解答

描述 示例 算法思路1 整体思路是&#xff0c;先读取特性的优先级和测试用例覆盖的特性列表&#xff0c;然后计算每个测试用例的优先级&#xff0c;并将其与测试用例的索引存储到二维数组中。最后按照优先级和索引排序&#xff0c;输出测试用例的索引&#xff0c;即为执行顺序。…

怎么避免电脑数据被拷贝?电脑如何禁用USB功能?

在无纸化办公的今天&#xff0c;很多重要数据都存放在电脑中。为了避免数据泄露&#xff0c;需要采用安全的方式保护电脑数据。那么&#xff0c;该如何避免电脑数据被拷贝呢&#xff1f;下面我们就来了解一下。 方法一&#xff1a;物理隔绝 物理隔绝是一种原始但有效的USB禁用…

C++初阶 | [九] list 及 其模拟实现

摘要&#xff1a;介绍 list 容器&#xff0c;list 模拟实现 list&#xff08;带头双向循环列表&#xff09; 导入&#xff1a;list 的成员函数基本上与 vector 类似&#xff0c;具体内容可以查看相关文档(cplusplus.com/reference/list/list/)&#xff0c;这里不多赘述。以下对…

leetcode 热题 100_两两交换链表中的节点

题解一&#xff1a; 迭代&#xff1a;设置节点指针存储结果&#xff0c;遍历链表&#xff0c;直接修改节点的指向。 class Solution {public ListNode swapPairs(ListNode head) {ListNode result new ListNode();result.next head;ListNode temp result;while (temp.next …

下载chromedrive,使用自动化

1、先看一下自己浏览器的版本 2、访问 https://googlechromelabs.github.io/chrome-for-testing/

基于单片机的家庭防盗报警系统

摘 要 随着社会的发展&#xff0c;人们生活水平的不断提高和家居用品的高档化&#xff0c;家庭安全隐患也随之增加&#xff0c;所以人们便进一步提高了对家庭的保护意识。因此&#xff0c;这就不得不促使安全防盗报警系统的普及与推广。 然而传统的防盗措施难以实现人们的需求…

网络基础 - 预备知识(协议、网络协议、网络传输流程、地址管理)

文章目录 1. 认识 协议2. 了解 网络协议2.1 引入 协议分层2.2 OSI 七层模型 与 TCP/IP 四层模型 3. 网络传输 流程&#xff01;&#xff01;&#xff01;3.1 网络传输流程图3.2 关于报头3.3 实例解释 传输过程&#xff08;封装与解包&#xff09; 4. 网络中的地址管理4.1 认识 …

EMQX 4.0和EMQX 5.0集群架构实现1亿MQTT连接哪些改进

EMQX 5.0水平扩展能力得到了指数级提升&#xff0c;能够更可靠地承载更大规模的物联网设备连接量。 在EMQX5.0正式发布前的性能测试中&#xff0c;我们通过一个23节点的EMQX集群&#xff0c;全球首个达成了1亿MQTT连接每秒100万消息吞吐&#xff0c;这也使得EMQX 5.0成为目前为…

如何利用百度SEO优化技巧将排到首页

拥有一个成功的网站对于企业和个人来说是至关重要的&#xff0c;在当今数字化的时代。在互联网上获得高流量和优质的访问者可能并不是一件容易的事情&#xff0c;然而。一个成功的SEO战略可以帮助你实现这一目标。需要一些特定的技巧和策略、但要在百度搜索引擎中获得较高排名。…