当前位置: 首页 > news >正文

C++(26): 原子操作(std::atomic)

目录

1. 简述

2. 什么是原子操作

3. C++原子操作

4. std::atomic_flag

5. std::atomic

(1)操作

(2)赋值(store)、读取(load)与交换(exchange)

(3)算术运算

(4)CAS


1. 简述

        开发过多线程、并发编程的小伙伴一定接触过mutex,通过对资源进行加锁和解锁,实现对方问和修改的互斥操作。mutex使用起来很方便,很强大,但也有局限性。频繁地加锁和解锁会造成较大的资源消耗,影像系统的性能。

        与mutex相比,原子(atomic)操作相对灵活和简单。

        注意,这种灵活性在一定程度上是做了某些妥协的。

2. 什么是原子操作

        原子指的是一系列不可被CPU上下文交换的机器指令,这些指令组合在一起就形成了原子操作。

        我们在日常使用的CPU或SOC基本都是多核的情况,当其中某个CPU核心开始运行原子操作时,会先暂停其它CPU内核对内存的操作,保证对资源的独占性,进而保证资源不会被其它CPU内核所干扰,这就是原子操作的通俗解释。

3. C++原子操作

        C++提供了一个模板类型std::atomic<T>来助力实现原子操作,还提供了一些特化的原子类型,例如std::atomic_int、std::atomic_long等。

        此外还提供了std::atomic_flag这一超简单的原子类型,简单到只有设置(set)和清除(clear)两种状态。

4. std::atomic_flag

        std::atomic_flag可以说得上是最简单的原子类型了,他只有设置(set)和清除(clear)两种状态。std::atomic_flag不可拷贝和赋值,且必须使用ATOMIC_FLAG_INIT宏初始化。

#include <atomic>std::atomic_flag flag = ATOMIC_FLAG_INIT;

        关于atomic_flag我们只需要掌握两个成员的使用就可以了,他们分别是test_and_set和clear。

        test_and_set用于判断当前变量是否被设置过,如果没有被设置过,则进行设置,并返回false,反之则直接返回true。

        clear用于清除设置的状态。

        如下所示的例程演示了排他性的访问某些资源,创建10个线程,分别访问同一段资源。当test_and_set返回true时,说明有其他某个线程正在访问,因此等待,知道test_and_set返回false,进行访问,之后清除。

#include <iostream>#include <atomic>#include <thread>#include <vector>#include <sstream>std::atomic_flag atomic_state = ATOMIC_FLAG_INIT;std::stringstream stream_info;void access_stream(int x){while (atomic_state.test_and_set()); ///< 等待状态被清除stream_info << "thread" << x << "access stream" << '\n';atomic_state.clear(); ///< 清楚状态}int main(){std::vector < std::thread > threads;for(int i = 1; i <= 10; ++i){threads.push_back(std::thread(access_stream, i));}for(auto & th:threads){th.join();}std::cout << stream_info.str() << std::endl;;return 0;}

5. std::atomic<T>

        std::atomic<T>作为一个模板,提供了通用的原子类型,也提供了比std::atomic_flag更为灵活和复杂的应用功能。

        std::atomic_int等作为特化的原子类型,是特殊的std::atomic<T>,一般来讲std::atomic_int等价于std::atomic<int>,其他特化类型类似。

(1)操作

        std::atomic提供了赋值、算术运算和比较交换等操作。

(2)赋值(store)、读取(load)与交换(exchange)

        std::tomic提供了store和load接口,分别用来赋值和读取,也提供了exchange用来交换新值,返回旧值。

#include <iostream>#include <atomic>int main(int argc, char* argv[]){std::atomic<int> atomic_int(0);atomic_int.store(10); ///< 设置原子变量的值std::cout << "value: " << atomic_int.load() << std::endl;int value = atomic_int.load(); ///< 读取原子变量的值std::cout << "value: " << value << std::endl;int old_value = atomic_int.exchange(20); // 交换原子变量的值std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;return 0;}

(3)算术运算

        std::atomic提供了原子加,原子减等接口,具体包含fetch_add、fetch_sub、fetch_and、fetch_or和fetch_xor等。

        原子算术运算后,都会返回原值。

#include <iostream>#include <atomic>int main(int argc, char* argv[]){std::atomic<int>    atomic_int(0);int last_value;last_value = atomic_int.fetch_add(10); ///< 原子加操作 std::cout << "last_value: " << last_value << ", new_value: " << atomic_int.load() << std::endl;last_value = atomic_int.fetch_sub(5); ///< 原子减操作std::cout << "last_value: " << last_value << ", new_value: " << atomic_int.load() << std::endl;last_value = atomic_int.fetch_and(0b1100); ///< 原子与操作std::cout << "last_value: " << last_value << ", new_value: " << atomic_int.load() << std::endl;last_value = atomic_int.fetch_or(0b1010); ///< 原子或操作std::cout << "last_value: " << last_value << ", new_value: " << atomic_int.load() << std::endl;last_value = atomic_int.fetch_xor(0b1111); ///< 原子异或操作std::cout << "last_value: " << last_value << ", new_value: " << atomic_int.load() << std::endl;return 0;}

(4)CAS

        CAS,Compare and Swap,比较并交换。

        std::atomic提供了compare_exchange_weak和compare_exchange_strong实现比较及交换功能,二者的功能是一样的,但是前者性能更好一些,常在高速循环中使用。

        参数传入期待值与新值,通过比较当前值与期待值的情况进行区别改变。

a.compare_exchange_weak(b, c)其中a是当前值,b期望值,c新值

a==b时:函数返回真,并把c赋值给a

a!=b时:函数返回假,并把a复制给b

#include <iostream>#include <atomic>int main(int argc, char* argv[]){std::atomic<int> a;a.store(10);int b=10;int c=20;std::cout<<"a:"<<a<<std::endl;if(a.compare_exchange_weak(b, c)){ ///< a和b值相同,把c的值赋给astd::cout<<"a true:"<<a.load()<<std::endl;}std::cout<<"a:"<<a<<" b:"<<b<<" c:"<<c<<std::endl;return 0;}>> 运行结果a: 20 b:10 c:20

相关文章:

  • 诺瓦星云入职认知能力SHL测验Verify职业性格问卷OPQ可搜索带解析求职题库
  • Java练习题4
  • 锂锗磷硫(LGPS)是代表性硫化物固态电解质产品之一 技术研究不断深入
  • python-题库篇-Python语言特性
  • 【计算机毕业设计】196运动健康weixin小程序
  • leetcode 动态规划(基础版)三角形最小路径和
  • JC/T 2752-2023 导(防)静电不发火地坪检测
  • 海外版coze前端代码助手
  • rs232和can的区别
  • python20 函数的定及调用
  • Java面试题:讨论synchronized关键字和java.util.concurrent包中的同步工具,如Lock和Semaphore
  • windows安装spark
  • Mysql特殊用法分享
  • platformio烧写STC8H1K08单片机程序失败:Serial port error: read timeout
  • 探索设计模式——单例模式详解
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • 【技术性】Search知识
  • Android系统模拟器绘制实现概述
  • Computed property XXX was assigned to but it has no setter
  • Linux后台研发超实用命令总结
  • Linux下的乱码问题
  • Nodejs和JavaWeb协助开发
  • Python socket服务器端、客户端传送信息
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • zookeeper系列(七)实战分布式命名服务
  • 翻译:Hystrix - How To Use
  • 关于for循环的简单归纳
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 日剧·日综资源集合(建议收藏)
  • 详解移动APP与web APP的区别
  • 小程序开发之路(一)
  • 一道面试题引发的“血案”
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • linux 淘宝开源监控工具tsar
  • 继 XDL 之后,阿里妈妈开源大规模分布式图表征学习框架 Euler ...
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​渐进式Web应用PWA的未来
  • # 飞书APP集成平台-数字化落地
  • (02)Hive SQL编译成MapReduce任务的过程
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (不用互三)AI绘画:科技赋能艺术的崭新时代
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (附源码)ssm智慧社区管理系统 毕业设计 101635
  • (附源码)计算机毕业设计ssm电影分享网站
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (转)平衡树
  • *算法训练(leetcode)第三十九天 | 115. 不同的子序列、583. 两个字符串的删除操作、72. 编辑距离
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .Net7 环境安装配置
  • .NET分布式缓存Memcached从入门到实战
  • .Net实现SCrypt Hash加密
  • .net最好用的JSON类Newtonsoft.Json获取多级数据SelectToken