c++原子变量

原子变量

概述

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

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

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

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

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

1.atomic类成员

类定义

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

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

构造函数
// ①
atomic() noexcept = default;
// ②
constexpr atomic( T desired ) noexcept;
// ③
atomic( const atomic& ) = delete;
  • 构造函数①:默认无参构造函数。
  • 构造函数②:使用 desired 初始化原子变量的值。
  • 构造函数③:使用=delete显示删除拷贝构造函数, 不允许进行对象之间的拷贝
公共成员函数

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

T operator=( T desired ) noexcept;
T operator=( T desired ) volatile noexcept;

atomic& operator=( const atomic& ) = delete;
atomic& operator=( const atomic& ) volatile = delete;

也就是说:

void test01() {
	atomic_int a = 0; // correct
     atomic_int d(0); // correct
	// atomic_int b = a; // error
	// atomic_int c(a); // error
}
store函数

store 函数用于将一个值存储到原子变量中。它的基本形式如下:

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;
  • desired:表示要存储到原子变量中的值。
  • order:表示存储操作的内存序(memory order),默认为 memory_order_seq_cst,即顺序一致性。(可不指定)
load函数

load 函数用于从原子变量中加载当前的值。它的基本形式如下:

T load(memory_order order = memory_order_seq_cst) const noexcept;

order:表示加载操作的内存序(memory order),默认为 memory_order_seq_cst,即顺序一致性。

两个函数示例:

#include <iostream>
using namespace std;
#include <atomic>
#include <thread>

atomic_int atomicCount(0);

void test02() {
	for (int i = 0; i < 100; ++i) {
		atomicCount.store(atomicCount.load() + 1);
	}

}

int main() {
	thread t1(test02);
	thread t2(test02);
	t1.join();
	t2.join();
	cout << atomicCount.load() << endl; // 200
}

在这个例子中,两个线程并发地对 atomicCounter 进行递增操作。由于 storeload 都是原子操作,因此可以确保对 atomicCounter 的操作是线程安全的。最后输出的 atomicCounter 的值是预期的 200。

特化成员函数

主要说的是赋值运算符重载:

在这里插入图片描述

以上各个 operator 都会有对应的 fetch_* 操作,详细见下表:

在这里插入图片描述

见代码:

void test01() {
	atomic_int a = 0; // correct
	atomic_int d(1); // correct
	// atomic_int b = a; // error
	// atomic_int c(a); // error
	a.store(10);
	++a;
	a += d;
	auto e = a & d;
	cout << e << endl; // 0
}
内存顺序约束

也就是load函数和store函数的参数 memory_order, 以指定如何同步不同线程上的其他操作。

定义如下:

typedef enum memory_order {
    memory_order_relaxed,   // relaxed
    memory_order_consume,   // consume
    memory_order_acquire,   // acquire
    memory_order_release,   // release
    memory_order_acq_rel,   // acquire/release
    memory_order_seq_cst    // sequentially consistent
} memory_order;
  • memory_order_relaxed, 这是最宽松的规则,它对编译器和CPU不做任何限制,可以乱序
  • memory_order_release 释放,设定内存屏障(Memory barrier),保证它之前的操作永远在它之前,但是它后面的操作可能被重排到它前面
  • memory_order_acquire 获取, 设定内存屏障,保证在它之后的访问永远在它之后,但是它之前的操作却有可能被重排到它后面,往往和Release在不同线程中联合使用
  • memory_order_consume:改进版的memory_order_acquire ,开销更小
  • memory_order_acq_rel,它是Acquire 和 Release 的结合,同时拥有它们俩提供的保证。比如你要对一个 atomic 自增 1,同时希望该操作之前和之后的读取或写入操作不会被重新排序
  • memory_order_seq_cst 顺序一致性, memory_order_seq_cst 就像是memory_order_acq_rel的加强版,它不管原子操作是属于读取还是写入的操作,只要某个线程有用到memory_order_seq_cst 的原子操作,线程中该memory_order_seq_cst 操作前的数据操作绝对不会被重新排在该memory_order_seq_cst 操作之后,且该memory_order_seq_cst 操作后的数据操作也绝对不会被重新排在memory_order_seq_cst 操作前。
c++20新增成员

在C++20版本中添加了新的功能函数,可以通过原子类型来阻塞线程,和条件变量中的等待/通知函数是一样的。

在这里插入图片描述

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;
}
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_int
    atomic_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;
}

程序运行结果:

decrement number: -1, theadID: 14916
increment number: 0, theadID: 6372
decrement number: -1, theadID: 14916
increment number: 0, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 0, theadID: 6372
increment number: 1, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 1, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 1, theadID: 6372
decrement number: 0, theadID: 14916
decrement number: 0, theadID: 14916
increment number: 0, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 0, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 0, theadID: 6372
decrement number: -1, theadID: 14916
increment number: 0, theadID: 6372

总结:

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

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

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

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

相关文章

如何用gpt改写文章 (1) 神码ai

大家好&#xff0c;今天来聊聊如何用gpt改写文章 (1)&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 如何用GPT改写文章 一、引言 随着人工智能技术的飞速发展&#xff0c;自然语言处理领域取得了重大突…

【一步到位】汽车过户全攻略:轻松搞定,告别繁琐流程

校长车行是一家昆明二手车代办公司&#xff0c;今天我们要聊一聊一个让很多人头疼的问题——汽车过户。相信很多朋友在购买二手车或者需要将车辆转让给他人时&#xff0c;都会遇到这个繁琐的流程。那么&#xff0c;如何才能轻松搞定汽车过户呢&#xff1f;接下来&#xff0c;就…

充电桩定期检测的必要性

充电桩是电动车充电的重要设备&#xff0c;其安全性直接关系到用户的生命财产安全。定期检测可以确保充电桩的正常运行&#xff0c;及时发现并修复潜在的安全隐患&#xff0c;提高充电桩的可靠性和安全性。 充电桩作为一个外部设备&#xff0c;长期暴露在室外环境中&#xff0c…

【cocotb】【达坦科技DatenLord】Cocotb Workshop分享

https://www.bilibili.com/video/BV19e4y1k7EE/?spm_id_from333.337.search-card.all.click&vd_sourcefd0f4be6d0a5aaa0a79d89604df3154a 方便RFM实现 cocotb_test 替代makefile &#xff0c; 类似python 函数执行

快宝技术:连接无代码开发,API集成提升电商营销和用户运营效率

无代码开发&#xff1a;创新的启航 快宝技术自2012年成立至今&#xff0c;一直是无代码开发领域的佼佼者。通过无代码开发平台&#xff0c;快宝技术旨在降低技术门槛&#xff0c;并使非技术人员能够轻松创建和部署应用程序。这不仅使得快递末端软件开发变得高效和便捷&#xf…

【51单片机系列】矩阵按键介绍

本节实现的功能是&#xff1a;通过开发板上的矩阵键盘控制静态数码管显示对应的键值0-F。 文章目录 一、矩阵按键介绍二、硬件设计三、软件设计 一、矩阵按键介绍 独立键盘与单片机连接时&#xff0c;每一个按键都需要单片机的一个I/O口。若某单片机系统需要较多按键&#xff…

快速上手linux | 一文秒懂Linux各种常用命令(中)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 一、常用目录操作1.1 mkdir 新建目录操作1.2 mrdir 删除空目录1.3 pwd 显示当前所在的工作目录 二、 文件操作命…

系统架构设计师教程(五)软件工程基础知识

软件工程基础知识 5.1 软件工程5.1.1 软件工程定义5.1.2 软件过程模型5.1.3 敏捷模型敏捷开发的特点敏捷方法的核心思想主要敏捷方法简介 5.1.4 统一过程模型 (RUP)RUP的生命周期RUP中的核心概念RUP的特点 5.1.5 软件能力成熟度模型 5.2 需求工程5.2.1 需求获取需求获取的基本步…

2024第一届中国最美乡村文旅产业峰会即将召开

2024第一届中国最美乡村文旅产业峰会将于2024年7月13-14日在京召开。百年变局下的国家经济发展&#xff0c;整体产业经济的下行&#xff0c;央国企及地方龙头企业&#xff0c;均遭遇到了前所未有的发展磨砺&#xff0c;特别是三年疫情后的文旅发展&#xff0c;更面临着一种洗牌…

【MySQL】安装和配置mysql

环境&#xff1a;Centos 7 删除不需要的环境 查看是否有正在运行的服务&#xff1a; ps ajx |grep mysqlps ajx |grep mariadb切换为root用户&#xff0c; 如果存在有服务 systemctl stop mariadb.service 或者 systemctl stop mysqld查看系统下的mysql安装包并删除&#xf…

C语言数据结构-----二叉树(2)堆的深入理解及应用、链式二叉树的讲解及代码实现

前言 本篇文章讲述的内容有部分是上一节写过的。重复内容不会再进行说明&#xff0c;大家可以看上一节内容 链接: C语言数据结构-----二叉树(1)认识数、二叉树、堆及堆的代码实现 文章目录 前言1.使用堆解决TOP-K问题2.向下调整堆的时间复杂度与向上调整堆的时间复杂度对比3.堆…

智能优化算法应用:基于树种算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于树种算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于树种算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.树种算法4.实验参数设定5.算法结果6.参考文献7.MA…

女人感染阴虱的表现?劲松中西医结合医院专家揭示4点

阴虱是一种常见的性传播疾病&#xff0c;由虱子寄生于阴部毛发引起。女性感染阴虱后&#xff0c;通常会有一些特定的症状和表现。了解这些症状有助于及时发现并治疗阴虱感染。 1. 瘙痒&#xff1a;阴虱叮咬阴部皮肤时会产生强烈的瘙痒感。瘙痒可能持续数天或数周&#xff0c;尤…

前端走向未来:真相还是焦虑的贩卖?

目录 一、为什么会出现“前端已死”的言论 二、你如何看待“前端已死” 三、前端技术的未来发展趋势 四、前端人&#xff0c;该如何打好这场职位突围战&#xff1f; 我的其他博客 一、为什么会出现“前端已死”的言论 近来&#xff0c;IT圈内流传着“Java 已死、前端已凉”…

宋仕强论道之华强北的硬件设施(二十三)

宋仕强论道之华强北的硬件设施&#xff08;二十三&#xff09;&#xff1a; 现在深圳市华强北&#xff08;Shenzhen Huaqiangbei&#xff09;硬件设施哪方面不足呢&#xff1f;以前华强北的“万家百货”和“女人世界”在生意最旺的时候&#xff0c;顾客要在门口排队进商场&…

Excel函数 - Filter函数六种查询用法

语法&#xff1a;filter(查询区域&#xff0c;条件&#xff0c;查不到结果返回的值) 注意条件是布尔表达式&#xff0c;这个条件为true&#xff0c;filter才返回结果&#xff0c;否则查不到结果。 一、单条件查询&#xff0c;返回多行多列记录 我们要查询“苹果”这个水果&a…

用Go汇编实现一个快速排序算法

本代码全网首发&#xff0c;使用Go plan9 windows arm64汇编&#xff0c;实现基础版快速排序算法。 未引入随机因子的快速排序的普通Go代码长这样。 func QuickSort(arr []int) {if len(arr) < 1 {return}base, l, r : arr[0], 0, len(arr)-1for i : 1; i < r; {if arr…

选择卓越的数据防泄密软件,关键评判标准揭秘!

如今数据安全环境不断遭受挑战&#xff0c;面对复杂、未知的数据泄露手段&#xff0c;使用数据防泄密软件成为当前企事业单位的最佳选择&#xff0c;而一款好的数据防泄密软件有哪些主要标准&#xff1f;迅软DSE带大家从以下几点判断。 1.稳定性&#xff1a;稳定性是产品选型最…

yml中“@project.description@“失效

pom文件中添加如下代码 <resources><resource><directory>src/main/resources</directory><filtering>true</filtering></resource> </resources> 效果如下&#xff1a; 原理&#xff1a; yml文件中类似 ${spring.applica…

SpringSecurity安全授权

目录 前言 正文 1.基本流程 2.基本用法 3.配置项 4.HttpSecurity 方式和内存认证方式 5.认证流程 6.基于数据库查询的登录验证 7.多种角色权限认证 8.自定义权限认证 总结 前言 安全对于任何系统来说都是非常重要的&#xff0c;权限的分配和管理一直都是开发者需…