C++11特性:原子变量

C++11提供了一个原子类型std::atomic<T>,通过这个原子类型管理的内部变量就可以称之为原子变量,我们可以给原子类型指定bool、char、int、long、指针等类型作为模板参数(不支持浮点类型和复合类型)。

原子指的是一系列不可被CPU上下文交换的机器指令,这些指令组合在一起就形成了原子操作。在多核CPU下,当某个CPU核心开始运行原子操作时,会先暂停其它CPU内核对内存的操作,以保证原子操作不会被其它CPU内核所干扰。

由于原子操作是通过指令提供的支持,因此它的性能相比锁和消息传递会好很多。相比较于锁而言,原子类型不需要开发者处理加锁和释放锁的问题,同时支持修改,读取等操作,还具备较高的并发性能,几乎所有的语言都支持原子类型。

可以看出原子类型是无锁类型,但是无锁不代表无需等待,因为原子类型内部使用了CAS循环,当大量的冲突发生时,该等待还是得等待!但是总归比锁要好。

C++11内置了整形的原子变量,这样就可以更方便的使用原子变量了。在多线程操作中,使用原子变量之后就不需要再使用互斥量来保护该变量了,用起来更简洁。因为对原子变量进行的操作只能是一个原子操作(atomic operation),原子操作指的是不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何的上下文切换。多线程同时访问共享资源造成数据混乱的原因就是因为CPU的上下文切换导致的,使用原子变量解决了这个问题,因此互斥锁的使用也就不再需要了。

CAS全称是Compare and swap, 它通过一条指令读取指定的内存地址,然后判断其中的值是否等于给定的前置值,如果相等,则将其修改为新的值。

1. atomic 类成员

1.1 类定义:

// 定义于头文件 <atomic>
template< class T >
struct atomic;

通过定义可得知:在使用这个模板类的时候,一定要指定模板类型。 

1.2 构造函数:

// ①
atomic() noexcept = default;
// ②
constexpr atomic( T desired ) noexcept;
// ③
atomic( const atomic& ) = delete;

构造函数①:默认无参构造函数。
构造函数②:使用 desired 初始化原子变量的值。
构造函数③:使用=delete显示删除拷贝构造函数, 不允许进行对象之间的拷贝 。

1.3 公共成员函数: 

原子类型在类内部重载了 操作符,并且不允许在类的外部使用进行对象的拷贝。

T operator=( T desired ) noexcept;
T operator=( T desired ) volatile noexcept;atomic& operator=( const atomic& ) = delete;
atomic& operator=( const atomic& ) volatile = delete;

原子地以 desired 替换当前值。按照 order 的值影响内存。 

void store( T desired, std::memory_order order = std::memory_order_seq_cst ) noexcept;
void store( T desired, std::memory_order order = std::memory_order_seq_cst ) volatile noexcept;

1. desired:存储到原子变量中的值。
2. order:强制的内存顺序 。

原子地加载并返回原子变量的当前值。按照 order 的值影响内存。直接访问原子对象也可以得到原子变量的当前值。

T load( std::memory_order order = std::memory_order_seq_cst ) const noexcept;
T load( std::memory_order order = std::memory_order_seq_cst ) const volatile noexcept;

1.4 类型别名: 

atomic_bool(C++11)    std::atomic<bool>
atomic_char(C++11)    std::atomic<char>
atomic_schar(C++11)    std::atomic<signed char>
atomic_uchar(C++11)    std::atomic<unsigned char>
atomic_short(C++11)    std::atomic<short>
atomic_ushort(C++11)    std::atomic<unsigned short>
atomic_int(C++11)    std::atomic<int>
atomic_uint(C++11)    std::atomic<unsigned int>
atomic_long(C++11)    std::atomic<long>
atomic_ulong(C++11)    std::atomic<unsigned long>
atomic_llong(C++11)    std::atomic<long long>
atomic_ullong(C++11)    std::atomic<unsigned long long>
atomic_char8_t(C++20)    std::atomic<char8_t>
atomic_char16_t(C++11)    std::atomic<char16_t>
atomic_char32_t(C++11)    std::atomic<char32_t>
atomic_wchar_t(C++11)    std::atomic<wchar_t>
atomic_int8_t(C++11)(可选)    std::atomic<std::int8_t>
atomic_uint8_t(C++11)(可选)    std::atomic<std::uint8_t>
atomic_int16_t(C++11)(可选)    std::atomic<std::int16_t>
atomic_uint16_t(C++11)(可选)    std::atomic<std::uint16_t>
atomic_int32_t(C++11)(可选)    std::atomic<std::int32_t>
atomic_uint32_t(C++11)(可选)    std::atomic<std::uint32_t>
atomic_int64_t(C++11)(可选)    std::atomic<std::int64_t>
atomic_uint64_t(C++11)(可选)    std::atomic<std::uint64_t>
atomic_int_least8_t(C++11)    std::atomic<std::int_least8_t>
atomic_uint_least8_t(C++11)    std::atomic<std::uint_least8_t>
atomic_int_least16_t(C++11)    std::atomic<std::int_least16_t>
atomic_uint_least16_t(C++11)    std::atomic<std::uint_least16_t>
atomic_int_least32_t(C++11)    std::atomic<std::int_least32_t>
atomic_uint_least32_t(C++11)    std::atomic<std::uint_least32_t>
atomic_int_least64_t(C++11)    std::atomic<std::int_least64_t>
atomic_uint_least64_t(C++11)    std::atomic<std::uint_least64_t>
atomic_int_fast8_t(C++11)    std::atomic<std::int_fast8_t>
atomic_uint_fast8_t(C++11)    std::atomic<std::uint_fast8_t>
atomic_int_fast16_t(C++11)    std::atomic<std::int_fast16_t>
atomic_uint_fast16_t(C++11)    std::atomic<std::uint_fast16_t>
atomic_int_fast32_t(C++11)    std::atomic<std::int_fast32_t>
atomic_uint_fast32_t(C++11)    std::atomic<std::uint_fast32_t>
atomic_int_fast64_t(C++11)    std::atomic<std::int_fast64_t>
atomic_uint_fast64_t(C++11)    std::atomic<std::uint_fast64_t>
atomic_intptr_t(C++11)(可选)    std::atomic<std::intptr_t>
atomic_uintptr_t(C++11)(可选)    std::atomic<std::uintptr_t>
atomic_size_t(C++11)    std::atomic<std::size_t>
atomic_ptrdiff_t(C++11)    std::atomic<std::ptrdiff_t>
atomic_intmax_t(C++11)    std::atomic<std::intmax_t>
atomic_uintmax_t(C++11)    std::atomic<std::uintmax_t>

2. 原子变量的使用

 2.1 互斥锁版本:

#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <functional>
using namespace std;struct Counter
{void increment(){for (int i = 0; i < 10; ++i){lock_guard<mutex> locker(m_mutex);m_value++;cout << "increment number: " << m_value<< ", theadID: " << this_thread::get_id() << endl;this_thread::sleep_for(chrono::milliseconds(100));}}void decrement(){for (int i = 0; i < 10; ++i){lock_guard<mutex> locker(m_mutex);m_value--;cout << "decrement number: " << m_value<< ", theadID: " << this_thread::get_id() << endl;this_thread::sleep_for(chrono::milliseconds(100));}}int m_value = 0;mutex m_mutex;
};int main()
{Counter c;auto increment = bind(&Counter::increment, &c);auto decrement = bind(&Counter::decrement, &c);thread t1(increment);thread t2(decrement);t1.join();t2.join();return 0;
}

示例程序的执行结果为(当然执行的结果不唯一):

decrement number: -1, theadID: 41376
decrement number: -2, theadID: 41376
decrement number: -3, theadID: 41376
decrement number: -4, theadID: 41376
decrement number: -5, theadID: 41376
increment number: -4, theadID: 25900
increment number: -3, theadID: 25900
increment number: -2, theadID: 25900
increment number: -1, theadID: 25900
increment number: 0, theadID: 25900
increment number: 1, theadID: 25900
increment number: 2, theadID: 25900
increment number: 3, theadID: 25900
increment number: 4, theadID: 25900
increment number: 5, theadID: 25900
decrement number: 4, theadID: 41376
decrement number: 3, theadID: 41376
decrement number: 2, theadID: 41376
decrement number: 1, theadID: 41376
decrement number: 0, theadID: 41376

2.2 原子变量版本: 

 

#include <iostream>
#include <thread>
#include <atomic>
#include <functional>
using namespace std;struct Counter
{void increment(){for (int i = 0; i < 10; ++i){m_value++;cout << "increment number: " << m_value<< ", theadID: " << this_thread::get_id() << endl;this_thread::sleep_for(chrono::milliseconds(500));}}void decrement(){for (int i = 0; i < 10; ++i){m_value--;cout << "decrement number: " << m_value<< ", theadID: " << this_thread::get_id() << endl;this_thread::sleep_for(chrono::milliseconds(500));}}// atomic<int> == atomic_intatomic_int m_value = 0;
};int main()
{Counter c;auto increment = bind(&Counter::increment, &c);auto decrement = bind(&Counter::decrement, &c);thread t1(increment);thread t2(decrement);t1.join();t2.join();return 0;
}

通过代码的对比可以看出,使用了原子变量之后,就不需要再定义互斥量了,在使用上更加简便,并且这两种方式都能保证在多线程操作过程中数据的正确性,不会出现数据的混乱。

原子类型atomic<T> 可以封装原始数据最终得到一个原子变量对象,操作原子对象能够得到和操作原始数据一样的效果,当然也可以通过store()load()来读写原子对象内部的原始数据。

 3. 效率的对比

 3.1 原子变量版本:

#include<iostream>
#include<memory>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<functional>
#include<atomic>std::atomic<int> shared_data;
void func()
{for (int i = 0; i < 100000; ++i){shared_data++;}
}int main()
{auto last = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();std::thread t1(func);std::thread t2(func);t1.join();t2.join();shared_data.store(666);auto cnt = shared_data.load();std::cout << "shared_data:" << cnt << std::endl;auto cur = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();std::cout << cur - last << std::endl;return 0;
}

执行的结果为:

shared_data:666
4447

其中消耗的微妙数为3000到5000。 

 3.2 互斥锁版本:

#include<iostream>
#include<memory>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<functional>
#include<atomic>int shared_data;
std::mutex mtx;
void func()
{for (int i = 0; i < 100000; ++i){std::lock_guard<std::mutex> locker(mtx);shared_data++;}
}int main()
{auto last = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();std::thread t1(func);std::thread t2(func);t1.join();t2.join();std::cout << "shared_data:" << shared_data << std::endl;auto cur = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();std::cout << cur - last << std::endl;return 0;
}

执行结果为:

shared_data:200000
14698

 其中消耗的微妙数为12000到20000。 

对比两种方式的消耗时间发现,使用原子量的效率更高

 本文参考:原子变量 | 爱编程的大丙 (subingwen.cn)

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

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

相关文章

Flask 页面布局layout_main.html

Flask 页面布局layout_main.html 【源码来自编程浪子的flask点餐小程序】 web/templates/common/layout_main.html <!DOCTYPE html> <html> <head><meta charset"utf-8"><meta name"viewport" content"widthdevice-wid…

应对微信小程序用户流失率的有效策略

随着移动互联网的发展&#xff0c;微信小程序已经成为许多企业和个人提供服务、产品的重要平台。然而&#xff0c;高用户流失率仍然是很多开发者面临的挑战之一。在这篇文章中&#xff0c;我们将讨论一些应对微信小程序用户流失率的有效策略&#xff0c;帮助开发者更好地留住用…

【C语言】程序练习(二)

大家好&#xff0c;这里是争做图书馆扫地僧的小白。 个人主页&#xff1a;争做图书馆扫地僧的小白_-CSDN博客 目标&#xff1a;希望通过学习技术&#xff0c;期待着改变世界。 目录 前言 一、运算符练习 1 算术运算符 1.1 练习题&#xff1a; 2 自加自减运算符 3 关系运…

Python列表数据处理全攻略(三):常用内置方法轻松掌握

文章目录 引言Python列表常用内置方法count()功能介绍语法示例注意事项 index()功能介绍语法示例注意事项&#xff1a; insert()功能介绍语法示例注意事项总结 结束语 引言 亲爱的读者&#xff0c;你好&#xff01;Python的列表在数据结构中占据着核心地位&#xff0c;对于学习…

机器学习——损失函数

【说明】文章内容来自《机器学习——基于sklearn》&#xff0c;用于学习记录。若有争议联系删除。 1、简介 损失函数(loss function)又称为误差函数(error function)&#xff0c;是衡量模型好坏的标准&#xff0c;用于估量模型的预测值与真实值的不一致程度&#xff0c;是一个…

基于SVM的冷却剂流量预测,基于支持向量机SVM的冷却剂流量预测

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 完整代码和数据下载链接: 基于SVM的冷却剂流量预测,基于支持向量机SVM的冷却剂流量预测(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835…

java itext5 生成PDF并填充数据导出

java itext5 生成PDF并填充数据导出 依赖**文本勾选框****页眉**&#xff0c;**页脚****图片**实际图 主要功能有文本勾选框&#xff0c;页眉&#xff0c;页脚&#xff0c;图片等功能。肯定没有专业软件画的好看&#xff0c;只是一点儿方法。仅供参考。 依赖 <!--pdf-->&…

12.18构建哈夫曼树(优先队列),图的存储方式,一些细节(auto,pair用法,结构体指针)

为结构体自身时&#xff0c;用.调用成员变量&#xff1b;为结构体指针时&#xff0c;用->调用成员变量 所以存在结构体数组时&#xff0c;调用数组元素里的成员变量&#xff0c;就是要用. 结构体自身只有在new时才会创建出来&#xff0c;而其指针可以随意创建 在用new时&…

Android Security PIN 相关代码

开发项目遇到一个问题&#xff0c;具体描述及复制步骤如下&#xff1a; 就是开启"Enhanced PIN privacy"(增强的PIN隐私)的时候输入秘密的时候还是会显示数字 如下图&#xff0c;应该是直接是“.” 不应该出现PIN 密码 想要的效果如下图&#xff1a; 设置的步骤如下图…

RabbitMQ 和 Kafka 对比

本文对RabbitMQ 和 Kafka 进行下比较 文章目录 前言RabbitMQ架构队列消费队列生产 Kafka本文小结 前言 开源社区有好多优秀的队列中间件&#xff0c;比如RabbitMQ和Kafka&#xff0c;每个队列都貌似有其特性&#xff0c;在进行工程选择时&#xff0c;往往眼花缭乱&#xff0c;不…

Flask 账号详情展示

Flask 账号详情展示 这段代码是一个基于Flask框架的Python应用程序。 它包含了两部分代码&#xff1a;Python代码和HTML代码。 web/templates/common/tab_account.html <div class"row border-bottom"><div class"col-lg-12"><div cla…

多态-多态的基本概念-类和对象

多态的基本 #include<iostream> using namespace std; //动物类 class Animal { public:virtual void Speak(){cout << " 动物在噢噢叫" << endl;} }; //猫类 class Cat :public Animal { public:void Speak(){cout << "小猫在噢噢叫&…

C++系列-第3章循环结构-26-认识do-while语句

C系列-第3章循环结构-26-认识do-while语句 在线练习&#xff1a; http://noi.openjudge.cn/ https://www.luogu.com.cn/ 对于 while 语句而言&#xff0c;如果不满足条件&#xff0c;则不能进入循环。但有时候我们需要即使不满足条件&#xff0c;也至少执行一次。 do-while循环…

CORS靶场安装测试记录

目录 环境 搭建 源码地址 环境 kali 搭建 1. 源码存放 /var/www/html 2. 创建数据库 mysql create database ica_lab; show databases; 3. SQL命令创建一个名为“billu”的新用户 create user admin@% identified by gesila; flush privileges; 4. 设置用户可远程登…

深入理解技术内容运营

技术内容营销 营销是一种商业策略&#xff0c;涉及识别客户需求并确定如何最好地满足这些需求。换句话说&#xff0c;它旨在确保企业或产品以吸引目标受众的方式定位&#xff0c;鼓励他们购买。该策略包含多个方面&#xff0c;包括市场研究、品牌建设、产品开发、销售、促销和…

购买腾讯云服务器需要多少钱?购买腾讯云服务器方法教程

腾讯云轻量应用服务器购买指南&#xff0c;有两个入口&#xff0c;一个是在特价活动上购买&#xff0c;一个是在轻量应用服务器官方页面购买&#xff0c;特价活动上购买价格更便宜&#xff0c;轻量2核2G3M带宽服务器62元一年起&#xff0c;阿腾云atengyun.com分享腾讯云轻量应用…

LeetCode 之算法篇(1)

本篇文章做leetcode top 100的的一些内容总结&#xff0c;以供后面复习使用 文章目录 题目41. 缺失的第一个正数 &#xff08;未掌握&#xff09;合并区间轮转数组238. 除自身以外数组的乘积 题目 41. 缺失的第一个正数 &#xff08;未掌握&#xff09; 这道题的关键点在于对桶…

Leetcod面试经典150题刷题记录 —— 区间篇

Leetcod面试经典150题刷题记录 —— 区间篇 1. 汇总区间2. 合并区间3. 插入区间4. 用最少数量的箭引爆气球 1. 汇总区间 题目链接&#xff1a;汇总区间 - leetcode 题目描述&#xff1a; 给定一个 无重复元素 的 有序 整数数组 nums 。返回 恰好覆盖数组中所有数字 的 最小有序…

算法导论复习纲要

函数 1. 上界下界&#xff0c;紧确界的定义 2. 求解递推式&#xff0c;代入法&#xff0c;递归树法&#xff0c;主方法 分治算法 动态规划 1. 切割钢条&#xff1a;递归方法&#xff0c;动态的自上而下&#xff0c; 2. 矩阵乘法&#xff1a;最优子结构性的证明&#xff0c…

Go语言学习

1、运行和解析 go run 命令已包含了编译和运行。它会先在一个临时目录编译程序&#xff0c;然后执行完后清理掉. 如果在run的后面加上 --work参数来查看临时目录。 go run --work main.go也可以通过go build命令来编译代码&#xff0c;执行后会生成一个可以执行文件&#xff…