C++中atomic的使用

atomic使用

  • 概述
  • 介绍
  • 使用场景
  • 头文件
  • atomic的使用
    • 创建
    • load()
    • store()
    • exchange()
    • compare_exchange_weak
    • compare_exchange_strong()
    • fetch_add()
    • fetch_sub()
    • fetch_and()
    • fetch_or()
    • fetch_xor()
  • 示例
    • 实现代码
    • 运行结果

概述

本文只要讲述C++11中atomic的使用,并通过示例来进一步的说明。

介绍

atomic在c++11中被引入,作为一种原子操作,之所以是原子操作,是因为其能保证操作过程不会被打断或终止,保证了被操作数据的安全性。原子操作是线程安全的。尤其在多线程同步中,常用于基本类型共享数据的保护。

原子操作:指的是不可被中断的一个或一系列操作,这些操作要么全部执行成功,要么全部不执行。

使用场景

  • 并发计数器:在多线程环境下,如果多个线程对同一个变量进行自增操作,可能会导致数据不一致。使用atomic可以确保自增操作的原子性,从而确保计数的准确性。

  • 读取和修改共享变量:当多个线程同时读取和修改同一个共享变量时,如果不使用原子操作,可能会导致数据冲突。使用atomic可以确保读取和修改操作的原子性,避免数据不一致。

  • 信号量:在多线程编程中,信号量常用于线程间的同步和通信。使用atomic可以确保对信号量的操作是原子的,从而避免出现竞态条件。

头文件

使用时需要头文件:

#include<atomic>

atomic的使用

创建

atomic是一个模板类型,可以用于基本数据类型的原子操作。非基本数据类型需要重新去实现一系列的复杂操作,不建议这样做。
以创建int型变量为例:

atomic<int> nAtomic;

此时nAtomic里的值为默认值0。

load()

实际上就是取出nAtomic的值:

nAtomic.load();//0

store()

向nAtomic变量中存储一个数值:

nAtomic.store(3);

exchange()

以原子方式交换变量的值,并返回旧值。

int nOld = nAtomic.exchange(5);

compare_exchange_weak

用于比较和交换两个数值。当nAtomic的当前值与期望值相等,更新为新值,否则,保持nAtomic中的值不变。

	int expired = 23;
    int newValue = 6;
    if (nAtomic.compare_exchange_weak(expired, newValue)) {//true当前值与期望值相等,设置为新值
        cout << "当前值与期望值相等,更新新值成功,g_countAtomic = " << nAtomic<< endl;
    }
    else {//当前值与期望值不想等,还是原来的值
        cout << "当前值与期望值不相等,没有更新新值,g_countAtomic = " << nAtomic<< endl;
    }

compare_exchange_strong()

与compare_exchange_weak()功能一样。区别是:

  1. 阻塞:如果比较和交换操作失败,函数通常会忙等待(busy-wait)直到条件满足为止。这意味着它会持续检查条件,直到可以安全地执行比较和交换为止。这种忙等待行为可能会导致 CPU 资源的浪费,因此在高并发场景中可能需要谨慎使用。

  2. 一致性保证:与 compare_exchange_weak() 相比,compare_exchange_strong() 提供更强的内存一致性保证。具体来说,它确保了在比较和交换操作成功执行后,其他线程将立即看到更新后的值。这是通过强制使用特定的内存排序规则来实现的,这些规则确保了在多线程环境中的可见性和顺序性。
    当多线程环境下,一般使用compare_exchange_weak()在一个循环中,确保没有被虚假的比较和交换失败。
    下面是一个使用循环的例子:

#include <iostream>  
#include <thread>  
#include <atomic>  
#include <chrono>  
  
std::atomic<int> counter(0); // 定义一个原子整数  
  
void increment_counter() {  
    int local_copy = counter.load(); // 读取当前值  
    while (true) {  
        int new_value = local_copy + 1; // 计算新值  
        if (counter.compare_exchange_weak(local_copy, new_value)) {  
            // 如果比较和交换成功,跳出循环  
            break;  
        }  
        // 如果比较和交换失败,重新读取当前值并继续循环  
    }  
}  
  
int main() {  
    const int num_threads = 10; // 使用10个线程来增加计数器  
    std::thread threads[num_threads];  
  
    // 创建并启动线程  
    for (int i = 0; i < num_threads; ++i) {  
        threads[i] = std::thread(increment_counter);  
    }  
  
    // 等待所有线程完成  
    for (int i = 0; i < num_threads; ++i) {  
        threads[i].join();  
    }  
  
    // 输出最终计数器的值  
    std::cout << "Final counter value: " << counter << std::endl;  
  
    return 0;  
}

如果 compare_exchange_weak() 失败(即返回 false),说明有其他线程在此期间修改了 counter 的值。此时,local_copy 仍然保持原来的值,而 counter 的值已经被其他线程更新。因此,函数重新读取 counter 的当前值到 local_copy 中,并继续循环,尝试再次进行比较和交换。

fetch_add()

原子的增减变量的值,并返回旧值。

int oldResult = nAtomic.fetch_add(7);

fetch_sub()

原子的减少变量的值,并返回旧值。

int oldFet = nAtomic.fetch_sub(4);

fetch_and()

原子的进行按位与,返回操作之前的值。

int oldAndValue = nAtomic.fetch_and(10);

fetch_or()

原子的进行按位或,返回操作之前的值。

int oldOrValue = nAtomic.fetch_or(10);

fetch_xor()

原子的进行按位异或,返回操作之前的值。

int oldXorValue = nAtomic.fetch_xor(13);

示例

下面是一个简单的使用atomic的程序。

实现代码

#include <iostream>
#include <atomic>

using namespace std;
atomic<int> g_countAtomic;

void fun() {
    if (!g_countAtomic.load()) {
        g_countAtomic.store(2);
        cout << "加载atomic的值:" <<g_countAtomic<< endl;
    }
    g_countAtomic.store(3);
    cout << "再次加载atomic的值:" << g_countAtomic << endl;
    int nOld = g_countAtomic.exchange(5);
    cout << "以前的值:"<<nOld<<" 再次加载atomic的值:" << g_countAtomic.load() << endl;
    int expired = 23;
    int newValue = 6;
    if (g_countAtomic.compare_exchange_weak(expired, newValue)) {//true当前值与期望值相等,设置为新值
        cout << "当前值与期望值相等,更新新值成功,g_countAtomic = " << g_countAtomic << endl;
    }
    else {//当前值与期望值不想等,还是原来的值
        cout << "当前值与期望值不相等,没有更新新值,g_countAtomic = " << g_countAtomic << endl;
    }
    int oldResult = g_countAtomic.fetch_add(7);
    cout << "以前的值:" << oldResult << " 增加7后atomic的值:" << g_countAtomic.load() << endl;
    int oldFet = g_countAtomic.fetch_sub(4);
    cout << "以前的值:" << oldFet << " 减去4后atomic的值:" << g_countAtomic.load() << endl;
    int oldAndValue = g_countAtomic.fetch_and(10);//1010
    cout << "按位与之前的值:" << oldAndValue <<" 按位与之后的值:"<<g_countAtomic.load() << endl;
    int oldOrValue = g_countAtomic.fetch_or(10);
    cout << "按位或之前的值:" << oldOrValue << " 按位或之后的值:" << g_countAtomic.load() << endl;
    int oldXorValue = g_countAtomic.fetch_xor(13);
    cout << "按位异或之前的值:" << oldXorValue << " 按位异或之后的值:" << g_countAtomic.load() << endl;
}


int main()  
{
    fun();
    cout << "----------------------------------" << endl;

    return 0;
}

运行结果

在这里插入图片描述

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

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

相关文章

一文读懂Prometheus和Grafana的区别(适合小白)

Prometheus和Grafana是两种开源软件&#xff0c;分别用于监控和可视化数据。它们的主要功能和特点如下&#xff1a; Prometheus 监控系统&#xff1a;Prometheus是一个专门用于收集和存储时间序列数据的监控系统。它可以从各种目标&#xff08;如服务器、数据库等&#xff09…

Spring中的事务和事务的传播机制

事务是一组操作的集合&#xff0c;不可以被分割。事务会把所有的操作作为一个整体&#xff0c;这组操作要么全部成功&#xff0c;要么全部失败。 事务有三种操作&#xff1a; 开启事务&#xff1b;提交事务&#xff1b;回滚事务。 如果代码的执行逻辑是这样&#xff1a; 开…

NutUI + taro +vue 开发遇到的问题 使用popup组件 内部元素滚动遇到的的问题

1 popup 弹出内容时 弹出的框内元素数据很长需要滚动时 本地可以正常滚动 打包成小程序后无法滚动 如这样的免责条款内容 代码如下 解决办法 1 把2处的单位换成百分比 弹框能滚动但是 是popup 里面所有的元素都一起滚动 导致标题都滚走了 2 scroll-y 改成&#xff1a; :scrol…

Excel数据表定制分组排序

实例需求&#xff1a;某学校体育活动统计表如下图左侧表格所示&#xff0c;数据按照班级排列&#xff0c;现在需要根据如下规格对表格进行排序 “幼儿”班级排列在表格最后按照“次数”降序排列“幼儿”班级同样按“次数”降序排列 排序结果如下图中右侧表格所示。 示例代码…

GDB之(8)GDB-Server远程调试

GDB之(8)GDB-Server远程调试 Author&#xff1a;Once Day Date&#xff1a;2024年2月27日 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-CSDN博客 参考文档: 用GDB调试程序 _CSDN博客 _陈皓GDB: The GNU Project Debugger…

UE4c++ ConvertActorsToStaticMesh ConvertProceduralMeshToStaticMesh

UE4c ConvertActorsToStaticMesh 创建Edior模块&#xff08;最好是放Editor模块毕竟是编辑器代码&#xff09;创建蓝图函数UBlueprintFunctionLibraryUTestFunctionLibrary.hUTestFunctionLibrary.cpp:.Build.cs 目标:为了大量生成模型&#xff0c;我们把虚幻带有的方法迁移成函…

【数据结构与算法】回溯法解题20240229

【数据结构与算法】回溯法解题20240229 一、46. 全排列1、以[1,2,3]为例&#xff0c;抽象成树形结构2、回溯三部曲 二、LCR 084. 全排列 II1、以[1,1,2]为例&#xff0c;抽象成树形结构 三、面试题 08.07. 无重复字符串的排列组合四、面试题 08.08. 有重复字符串的排列组合 一、…

基于视觉识别的自动采摘机器人设计与实现

一、前言 1.1 项目介绍 【1】项目功能介绍 随着科技的进步和农业现代化的发展&#xff0c;农业生产效率与质量的提升成为重要的研究对象。其中&#xff0c;果蔬采摘环节在很大程度上影响着整个产业链的效益。传统的手工采摘方式不仅劳动强度大、效率低下&#xff0c;而且在劳…

163邮箱SMTP端口号及服务器地址详细设置?

163邮箱SMTP端口号是什么&#xff1f;163邮件SMTP设置教程&#xff1f; 除了基本的邮箱账号和密码外&#xff0c;还需要了解SMTP服务器地址和端口号&#xff0c;以及相应的设置。这些设置对于确保邮件能够顺利发送至关重要。下面&#xff0c;蜂邮EDM将详细介绍163邮箱SMTP端口…

http和https的区别是什么?

–前言 传输信息安全性不同、连接方式不同、端口不同、证书申请方式不同 一、传输信息安全性不同 1、http协议&#xff1a;是超文本传输协议&#xff0c;信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文&#xff0c;就可以直接读懂其中的信息。 2、h…

《Redis 设计与实现》读书概要

注&#xff1a; 《Redis 设计与实现》一书基于 Redis 2.9 版本编写&#xff0c;部分内容已过时&#xff0c;过时之处本文会有所说明。本文为读书笔记&#xff0c;部分简单和日常使用较少的知识点未记录。原书网页版地址 https://redisbook.com/ 一、底层数据结构 SDS(Simple Dy…

微信小程序固定头部-CSS实现

效果图 代码逻辑&#xff1a;设置头部的高度&#xff0c;浮动固定后&#xff0c;再加个这个高度的大小的外边距 .weui-navigation-bar {position: fixed;top: 0px;left: 0px;right: 0px;height:90px; } .weui-navigation-bar_bottom{height:90px; }

【React架构 - Scheduler中的MessageChannel】

前序 我们都知道JS代码是在浏览器5个进程(下面有介绍)中渲染进程中的Js引擎线程执行的&#xff0c;其他还有GUI渲染线程、定时器线程等&#xff0c;而页面的布局和绘制是在GUI线程中完成的&#xff0c;这些线程之间是互斥的&#xff0c;所以在执行Js的同时会阻塞页面的渲染绘制…

仿牛客网项目---社区首页的开发实现

从今天开始我们来写一个新项目&#xff0c;这个项目是一个完整的校园论坛的项目。主要功能模块&#xff1a;用户登录注册&#xff0c;帖子发布和热帖排行&#xff0c;点赞关注&#xff0c;发送私信&#xff0c;消息通知&#xff0c;社区搜索等。这篇文章我们先试着写一下用户的…

数据可视化之旅:电商销售看板的制作与心得

作为一名电商工作者&#xff0c;每天都需要与海量的销售数据打交道。在数据海洋中&#xff0c;如何快速准确地找到关键信息&#xff0c;优化销售策略&#xff0c;是摆在我面前的一大挑战。在了解市面上众多可视化产品后&#xff0c;我选择尝试了山海鲸可视化软件&#xff0c;这…

2024年小程序云开发CMS内容管理无法使用,无法同步内容模型到云开发数据库的解决方案,回退老版本CMS内容管理的最新方法

一&#xff0c;问题描述 最近越来越多的同学找石头哥&#xff0c;说cms用不了&#xff0c;其实是小程序官方最近又搞大动作了&#xff0c;偷偷的升级的云开发cms&#xff08;内容管理&#xff09;以下都称cms&#xff0c;不升级不要紧&#xff0c;这一升级&#xff0c;就导致我…

【架构之路】糟糕程序员的20个坏习惯,切记要改掉

文章目录 强烈推荐前言&#xff1a;坏习惯:总结&#xff1a;强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站:人工智能 前言&#xff1a; 优秀的程序员…

matlab实现不同窗滤波器示例

1 汉明窗低通滤波器 &#xff1a; 在Matlab中使用汉明窗设计低通滤波器可以通过fir1函数实现。汉明窗通常用于设计滤波器&#xff0c;可以提供更突出的频率特性。 下面是一个示例代码&#xff0c;演示如何在Matlab中使用汉明窗设计低通滤波器&#xff1a; % 定义滤波器参数 fs …

Scrapy与分布式开发:框架原生去重机制源码解析与不足分析

框架原生去重机制源码解析与不足分析 导语 在网络爬虫和数据采集领域,去重机制是一个至关重要的环节。随着互联网的迅速发展,数据量呈爆炸式增长,如何在海量数据中高效地筛选出有价值且唯一的信息,成为了一个亟待解决的问题。去重机制正是为了解决这一问题而诞生的。 Sc…

docker中hyperf项目配置虚拟域名

在使用hyperf框架时&#xff0c;直接用了docker环境进行开发 下载镜像运行容器 docker run --name hyperf -v /data/project:/data/project -p 9501:9501 -itd -w /data/project --privileged -u root --entrypoint /bin/sh 镜像ID配置docker-compose.yml version: "3.…