C++ 原子变量
C++ 原子变量
文章目录
- C++ 原子变量
- 1. 原子变量是什么?
- 2. 原子操作的特点
- 3. 原子变量的作用
- 1. 多线程安全的共享数据访问
- 2. 替代锁机制
- 3. 实现低级同步算法
- 4. 原子变量的常见操作
- 5. 内存顺序(Memory Ordering)
- 内存顺序控制在原子变量中的作用
- 如何影响程序行为
- 6. 示例:比较并交换(CAS)
- 7. 总结
在 C++11 中,原子变量(atomic variables)是指通过
std::atomic 类型封装的变量,它们的操作在多线程环境中是 原子的,即不可分割的。这意味着对原子变量的操作(如读取、写入、更新等)是线程安全的,不会被其他线程的操作干扰或中断。
1. 原子变量是什么?
原子变量是 C++11 引入的,用于在多线程程序中确保对共享数据的访问是安全的。使用 std::atomic 类型,可以对变量进行原子操作,从而避免了传统的锁机制(如互斥锁 std::mutex)的使用。
std::atomic 是一个模板类,支持多种数据类型(如 int, bool, pointer 等)。它保证对该变量的操作是原子的,即所有操作要么完全成功,要么完全失败,不会被中断、重排或与其他线程的操作发生冲突。
2. 原子操作的特点
原子操作有几个显著的特点:
- 不可分割:原子操作要么完全执行,要么完全不执行,不会被其他线程的操作打断。
- 线程安全:由于原子性,多个线程可以同时访问同一个原子变量,而无需显式地加锁(如
std::mutex)。这对于提升并发性能非常重要。 - 避免数据竞争:通过确保每个操作是原子性的,避免了数据竞争(data race)的问题。
3. 原子变量的作用
原子变量在并发编程中起着非常重要的作用,它们的主要用途包括:
1. 多线程安全的共享数据访问
在多线程程序中,多个线程可能会同时访问并修改同一共享数据。使用常规的变量时,可能会发生数据竞争(data race),导致数据不一致或者程序崩溃。而原子变量可以确保对其的操作是原子的,从而避免了数据竞争。
例如,下面的代码演示了如何使用原子变量来安全地进行递增操作:
#include <iostream>
#include <atomic>
#include <thread>std::atomic<int> counter(0); // 定义一个原子变量void increment() {for (int i = 0; i < 10000; ++i) {counter.fetch_add(1, std::memory_order_relaxed); // 原子加 1}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final counter value: " << counter.load() << std::endl; // 安全读取原子变量return 0;
}
在上述代码中,两个线程 t1 和 t2 都在对 counter 进行自增操作。由于 counter 是原子变量,这样的操作是线程安全的,避免了数据竞争。
2. 替代锁机制
原子操作通过硬件提供的原子指令,能够高效地完成一些常见的同步任务,例如递增、递减、交换、比较和交换等操作,而不需要使用传统的锁机制(如 std::mutex)。这可以有效减少锁的使用,提高程序的并发性能。
传统的锁机制(如互斥锁)通常会引入线程上下文切换和性能开销,而原子变量的操作是直接通过硬件实现的,通常具有更高的效率。
3. 实现低级同步算法
在一些低级的并发数据结构和算法中,原子变量常常用于实现高效的同步机制,例如无锁队列、栈、哈希表等。通过原子操作,可以避免锁的使用,从而提高并发度和程序的整体性能。
4. 原子变量的常见操作
C++11 中的 std::atomic 提供了多种原子操作,这些操作可以保证对原子变量的修改是线程安全的。常见的操作包括:
load(): 读取原子变量的值。store(): 设置原子变量的值。exchange(): 将原子变量的值替换为指定值,并返回原先的值。compare_exchange_weak()/compare_exchange_strong(): 比较并交换(CAS,Compare and Swap)操作,只有当当前值等于预期值时才会交换。fetch_add()/fetch_sub(): 原子地执行加法或减法。fetch_and()/fetch_or()/fetch_xor(): 原子地执行按位与、按位或、按位异或。
例如:
std::atomic<int> value(0);// 原子加法
value.fetch_add(1, std::memory_order_relaxed);// 比较并交换
int expected = 0;
value.compare_exchange_weak(expected, 1);// 读取原子变量
int current_value = value.load();
5. 内存顺序(Memory Ordering)
std::atomic 操作有一个重要的概念——内存顺序。内存顺序控制了操作在多线程程序中的可见性和执行顺序。内存顺序控制决定了在多线程程序中,不同线程间对共享内存的操作顺序。因为现代处理器在执行程序时,可能出于性能考虑进行指令重排和缓存优化,这可能导致不同线程间看到的内存访问顺序不同。
例如,一个线程修改了某个变量的值,另一个线程读取该变量时,可能会看到不同的值,这就是由于内存重排或缓存的原因。为了解决这个问题,可以通过内存顺序控制来指定操作的顺序和可见性,以确保线程之间的同步和数据一致性。
C++11 提供了几种内存顺序选项:
memory_order_relaxed: 仅保证原子性,不强制同步顺序。memory_order_consume: 保证当前操作依赖的所有操作在其前面执行。memory_order_acquire: 保证当前操作之前的所有操作不会被重排。memory_order_release: 保证当前操作之后的所有操作不会被重排。memory_order_acq_rel: 同时具有acquire和release的效果。memory_order_seq_cst: 默认的内存顺序,保证所有操作的严格顺序一致。
内存顺序控制在原子变量中的作用
在使用原子变量时,操作不仅仅涉及到对数据的修改,还需要控制操作的可见性(不同线程是否看到相同的值)和顺序性(操作的执行顺序)。
C++11、Java等语言的原子操作库提供了对内存顺序的明确控制,通常有以下几种内存顺序:
- 顺序一致性(Sequentially Consistent,
memory_order_seq_cst)- 默认内存顺序。保证所有线程对原子变量的所有操作按照严格的顺序进行,所有线程都能看到一致的执行顺序。这种顺序是最强的同步保证,但也可能牺牲性能。
- 获取-释放(Acquire-Release,
memory_order_acquire和memory_order_release)- 获取(Acquire):用于确保当前线程在执行某个操作之前,所有前面的操作完成。这通常用于读取原子变量时,确保读取到最新的数据。
- 释放(Release):用于确保当前线程在执行某个操作之后,所有之后的操作完成。这通常用于写入原子变量时,确保所有更新已被其他线程可见。
- 获取-释放组合(
memory_order_acq_rel):同时包含获取和释放的功能。它常见于涉及原子变量的加减操作,确保操作前后的顺序。
- 无序(Unordered,
memory_order_relaxed)- 不对操作的顺序进行约束。线程对原子变量的操作不需要遵循任何内存顺序的规则,这样可以提高性能,但可能导致不同线程看到不一致的内存状态。
- 明确顺序(Ordered,
memory_order_consume和memory_order_acquire)memory_order_consume比较少用,通常被memory_order_acquire替代。其作用是确保之前的数据读取操作先于后续的操作。
如何影响程序行为
通过内存顺序控制,程序员可以精细控制线程间的同步,避免线程间的数据竞争,同时也能够优化程序性能。
- 内存顺序较弱(如
memory_order_relaxed):适用于某些不需要严格同步的场景,可以提高性能,因为它不强制执行指令顺序。 - 内存顺序较强(如
memory_order_seq_cst):适用于需要高度一致性的场景,如多个线程修改共享数据时,需要严格的顺序保证。
#include <atomic>
#include <iostream>
#include <thread>std::atomic<int> data = 0;void producer() {data.store(42, std::memory_order_release); // Release write
}void consumer() {while (data.load(std::memory_order_acquire) != 42) { // Acquire readstd::this_thread::sleep_for(std::chrono::milliseconds(10));}std::cout << "Data is: " << data.load() << std::endl;
}int main() {std::thread t1(producer);std::thread t2(consumer);t1.join();t2.join();return 0;
}
在这个示例中,producer线程将数据写入原子变量并使用memory_order_release,而consumer线程在读取时使用memory_order_acquire,这样可以确保consumer线程在读取数据时,所有写入data的操作都已经完成。原子变量的操作通过内存顺序控制来保证多线程程序中的正确性与性能。不同的内存顺序(如acquire、release、seq_cst等)允许程序员控制操作的同步方式,从而满足不同的并发需求。
6. 示例:比较并交换(CAS)
#include <iostream>
#include <atomic>std::atomic<int> counter(0);bool compare_exchange_example() {int expected = 0;return counter.compare_exchange_weak(expected, 1); // 如果当前值是 0,则将其更改为 1
}int main() {bool success = compare_exchange_example();std::cout << "Exchange success: " << success << ", new counter value: " << counter.load() << std::endl;return 0;
}
7. 总结
原子变量 (std::atomic) 在 C++11 中的引入,主要用于支持多线程程序中的共享数据的安全操作。通过 std::atomic 类型,可以避免使用传统的锁机制来保证线程安全,从而提高程序的并发性和性能。它们的主要用途包括确保多线程环境中共享数据的正确访问,替代锁机制,和在一些低级同步算法中的应用。
相关文章:
C++ 原子变量
C 原子变量 文章目录 C 原子变量1. 原子变量是什么?2. 原子操作的特点3. 原子变量的作用1. 多线程安全的共享数据访问2. 替代锁机制3. 实现低级同步算法 4. 原子变量的常见操作5. 内存顺序(Memory Ordering)内存顺序控制在原子变量中的作用如…...
linux网络 | http结尾、理解长连接短链接与cookie
前言:本节是http章节的最后一部分,主要解释一些小概念。讲解到了HTTP的方法,表单, 重定向等等。 现在废话不多说, 开始我们的学习吧。 ps:本节内容都是概念, 知道就行, 友友们放心观…...
金融项目实战 02|接口测试分析、设计以及实现
目录 ⼀、接口相关理论 二、接口测试 1、待测接口:投资业务 2、接口测试流程 3、设计用例理论 1️⃣设计方法 2️⃣工具 4、测试点提取 5、测试用例 ⼀、接口相关理论 1、ui功能测试和接⼝测试那个先执⾏?为什么? 结论:…...
二、智能体强化学习——深度强化学习核心算法
2.1 DQN 系列及其改进 2.1.1 背景与动机 在经典强化学习中(如 Q-Learning),如果状态空间或动作空间非常大乃至连续,那么用一个表格来存储 Q ( s , a ) Q(s,a) Q(s,a) 不再可行。为了解决该问题,可以使用神经网络来逼…...
Mysql--架构篇--存储引擎InnoDB(内存结构,磁盘结构,存储结构,日志管理,锁机制,事务并发控制等)
MySQL是一个多存储引擎的数据库管理系统,支持多种不同的存储引擎。每种存储引擎都有其独特的特性、优势和适用场景。选择合适的存储引擎对于优化数据库性能、确保数据完整性和满足业务需求至关重要。 注:在同一个Mysql的数据库中,对于不同的表…...
JVM实战—13.OOM的生产案例
大纲 1.每秒仅上百请求的系统为何会OOM(RPC超时时间设置过长导致QPS翻几倍) 2.Jetty服务器的NIO机制如何导致堆外内存溢出(S区太小 禁NIO的显式GC) 3.一次微服务架构下的RPC调用引发的OOM故障排查实践(MAT案例) 4.一次没有WHERE条件的SQL语句引发的OOM问题排查实践(使用MA…...
client-go 的 QPS 和 Burst 限速
1. 什么是 QPS 和 Burst ? 在 kubernetes client-go 中,QPS 和 Burst 是用于控制客户端与 Kubernetes API 交互速率的两个关键参数: QPS (Queries Per Second) 定义:表示每秒允许发送的请求数量,即限速器的平滑速率…...
使用docker-compose安装Redis的主从+哨兵模式
必看 本文是一主二从一哨兵模式;其余的单机/集群/多哨兵模式的话,不在本文... 本文的环境主要是:应用app在本地,redis在云服务器上; 图解 图如下:这个图很重要; 之所以要这样画图࿰…...
数据结构(Java版)第七期:LinkedList与链表(二)
专栏:数据结构(Java版) 个人主页:手握风云 一、链表的实现(补) 接上一期,下面我们要实现删除所有值为key的元素,这时候有的老铁就会想用我们上一期中讲到的remove方法,循环使用remove方法&#…...
ant-design-vue 1.X 通过id获取a-input组件失败
1.ant-design-vue 1.X 问题描述 当我在a-form组件中,以v-decorator指令绑定表单组件时,无法根据我设置的verify-code-input获取元素 <a-input type"text" id"verify-code-input" class"paIpt":placeholder"$t(…...
Flutter:吸顶效果
在分页中,实现tab吸顶。 TDNavBar的screenAdaptation: true, 开启屏幕适配。 该属性已自动对不同手机状态栏高度进行适配。我们只需关注如何实现吸顶。 view import package:ducafe_ui_core/ducafe_ui_core.dart; import package:flutter/material.dart; import p…...
MATLAB语言的数据类型
MATLAB语言的数据类型详解 MATLAB(矩阵实验室)是一种广泛应用于科学计算、数据分析、算法开发及模型构建的高性能语言和环境。MATLAB的强大之处不仅在于其丰富的数学工具和可视化功能,还有其灵活多变的数据类型。这篇文章将详细介绍MATLAB中…...
priority_queue优先队列
目录 1. 最短路径算法(Dijkstra算法) 应用场景: 优先队列的作用: 2. 最小生成树算法(Prim算法) 应用场景: 优先队列的作用: 3. 哈夫曼编码(Huffman Coding&#x…...
HarmonyOS 鸿蒙Next 预览pdf文件
HarmonyOS 鸿蒙Next 预览pdf文件 1、使用filePreview 2、使用web组件 在线pdf(网址是直接下载的,不是直接可以预览的),先下载再预览 import media from ohos.multimedia.media;import web_webview from ohos.web.webview;import …...
vscode开启调试模式,结合Delve调试器调试golang项目详细步骤
1.前期准备 (1).在vs code中的扩展程序中搜索并安装Go扩展程序 (2).安装 Delve 调试器 go install github.com/go-delve/delve/cmd/dlvlatest (3).打开vs code的命令面板,输入Go: Install/Update Tools,并单击该命令执行,安装或更新Go语…...
身份鉴权(PHP)(小迪网络安全笔记~
免责声明:本文章仅用于交流学习,因文章内容而产生的任何违法&未授权行为,与文章作者无关!!! 附:完整笔记目录~ ps:本人小白,笔记均在个人理解基础上整理,…...
【git】-初始git
一、什么是版本控制? 二、Git的安装 三、掌握Linux常用命令 四、Git基本操作 1、提交代码 2、查看历史提交 3、版本回退 一、什么是版本控制? 版本控制是一种用于记录文件或项目内容变化的系统。它通过版本标识和版本历史记录来管理不同版本&#…...
CSS 盒模型
盒模型 CSS盒模型是网页布局的核心概念之一,它描述了网页元素的物理结构和元素内容与周围元素之间的关系。根据W3C规范,每个HTML元素都被视为一个矩形盒子,这个盒子由以下四个部分组成: 内容区(Content areaÿ…...
[0405].第05节:搭建Redis主从架构
Redis学习大纲 一、3主3从的集群配置: 1.1.集群规划 1.分片集群需要的节点数量较多,这里我们搭建一个最小的分片集群,包含3个master节点,每个master包含一个slave节点,结构如下: 2.每组是一主一从&#x…...
6 分布式限流框架
限流的作用 在API对外互联网开放的情况下,是无法控制调用方的行为的。当遇到请求激增或者黑客攻击的情况下,会导致接口占用大量的服务器资源,使得接口响应效率的降低或者超时,更或者导致服务器宕机。 限流是指对应用服务进行限制…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...
