C++之std::atomic解决多线程7个问题(二百四)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!
优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

1.前言
本篇目的:理解C++之std::atomic原子操作解决多线程竟态问题用法。
2.std::atomic可以解决的C++多线程问题
在C++中,std::atomic提供了一种线程安全的访问原子类型的机制,可以解决以下七个多线程之间的问题:
- 竞态条件(Race Condition):多个线程同时访问和修改同一个变量时可能导致数据错误。
- 内存可见性(Memory Visibility):不同线程对共享变量的修改可能不可见,导致读取到过期的值。
- 乱序执行(Out-of-Order Execution):处理器可能以不同的顺序执行指令,导致结果的不确定性。
- 死锁(Deadlock):多个线程相互等待对方释放资源,导致程序无法继续执行。
- 活锁(Live Lock):多个线程相互响应对方的动作,而无法继续向前推进。
- 数据竞争(Data Race):多个线程同时访问和修改共享数据,没有同步机制可能导致不确定的行为。
- 优先级反转(Priority Inversion):高优先级任务被低优先级任务持续占用共享资源,导致高优先级任务无法及时执行。
3.应用实例
- 竞态条件(Race Condition):
v1.0 未使用atomic原子操作
#include <atomic>
#include <thread>
#include <cstdio>//std::atomic<int> counter(0); // 原子变量
int counter = 0; // 普通变量void increment() {for (int i = 0; i < 1000; i++) {counter++; // 原子操作:递增printf("counter = %d\n",counter);//打印出来的数据是乱序的}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();return 0;
}
v2.0 使用atomic原子操作
#include <atomic>
#include <thread>std::atomic<int> counter(0); // 原子变量void increment() {for (int i = 0; i < 1000; i++) {counter++; // 原子操作:递增printf("counter = %d\n",counter.load());//按顺序打印0 - 2000. }
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();return 0;
}
多个线程同时对counter进行递增操作,使用std::atomic可以避免竞态条件,保证递增操作的原子性。
- 内存可见性(Memory Visibility):
#include <atomic>
#include <thread>
#include <iostream>std::atomic<bool> flag(false); // 原子标志void run() {while (!flag.load()) {// do something}printf("flag = %d\n",flag.load());
}int main() {std::thread t(run);// 假设这里执行一些耗时的计算std::this_thread::sleep_for(std::chrono::seconds(1));flag.store(true); // 原子操作:标志位置为truet.join();return 0;
}
flag作为一个标志位,在一个线程中修改为true,其他线程可以通过读取flag的原子操作来感知到修改,保证内存的可见性。
- 乱序执行(Out-of-Order Execution):
#include <atomic>
#include <thread>
#include <iostream>std::atomic<int> value(0); // 原子变量void write() {value.store(42, std::memory_order_relaxed); // 原子操作:无序存储
}void read() {while (value.load(std::memory_order_relaxed) == 0) {// do something}std::cout << "Value: " << value.load(std::memory_order_relaxed) << std::endl;
}int main() {std::thread t1(write);std::thread t2(read);t1.join();t2.join();return 0;
}
通过使用适当的内存顺序(memory_order)来进行原子操作,可以避免乱序执行带来的问题,例如使用std::memory_order_relaxed来指定无序存储。
- 死锁(Deadlock):
#include <atomic>
#include <mutex>
#include <thread>std::atomic<bool> flag1(false);
std::atomic<bool> flag2(false);std::mutex mutex1;
std::mutex mutex2;void process1() {mutex1.lock(); // 获取锁1// 假设这里执行一些操作flag1.store(true); // 标记为已处理mutex2.lock(); // 获取锁2// 执行需要锁2的操作mutex2.unlock();mutex1.unlock();
}void process2() {mutex2.lock(); // 获取锁2// 假设这里执行一些操作flag2.store(true); // 标记为已处理mutex1.lock(); // 获取锁1// 执行需要锁1的操作mutex1.unlock();mutex2.unlock();
}int main() {std::thread t1(process1);std::thread t2(process2);t1.join();t2.join();return 0;
}
多个线程对两个互斥量(mutex)进行获取操作,可能导致死锁,std::atomic不能直接解决死锁问题,但可以用来作为线程间的通信机制。
- 活锁(Live Lock):
#include <atomic>
#include <thread>
#include <iostream>std::atomic<bool> flag1(false);
std::atomic<bool> flag2(false);void process1() {while (!flag2) {// 假设这里执行一些操作// 暂停一段时间,避免忙等待std::this_thread::sleep_for(std::chrono::milliseconds(100));flag1.store(true);// 假设这里还有其他判断逻辑}
}void process2() {while (!flag1.load()) {// 假设这里执行一些操作// 暂停一段时间,避免忙等待std::this_thread::sleep_for(std::chrono::milliseconds(100));flag2.store(true);// 假设这里还有其他判断逻辑}
}int main() {std::thread t1(process1);std::thread t2(process2);t1.join();t2.join();std::cout << "Live lock detected!" << std::endl;return 0;
}
两个线程互相等待对方设置标志位,但由于不断地执行操作和忙等待,两个线程无法继续前进,造成活锁。
- 数据竞争(Data Race):
#include <atomic>
#include <thread>
#include <iostream>std::atomic<int> counter(0); // 原子计数器void increment() {for (int i = 0; i < 1000; i++) {int temp = counter.load(); // 读取计数器的当前值counter.store(temp + 1); // 原子操作:递增}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Counter: " << counter.load() << std::endl;return 0;
}
两个线程同时读取和修改计数器的值,使用std::atomic可以避免数据竞争,保证计数器的正确递增。
- 优先级反转(Priority Inversion):
#include <atomic>
#include <thread>
#include <iostream>std::atomic<int> sharedResource(0); // 共享资源void lowPriorityThread() {while (true) {// 低优先级任务需要访问共享资源while (sharedResource.load() == 0) {// do something}std::cout << "Low priority thread accessing shared resource." << std::endl;// 假设这里执行一些低优先级任务的操作// 完成低优先级任务后释放共享资源sharedResource.store(0);}
}void highPriorityThread() {// 高优先级任务需要持有共享资源sharedResource.store(1);std::cout << "High priority thread acquired shared resource." << std::endl;// 假设这里执行一些高优先级任务的操作// 完成高优先级任务后释放共享资源sharedResource.store(0);
}int main() {std::thread t1(lowPriorityThread);std::thread t2(highPriorityThread);t1.join();t2.join();return 0;
}
相关文章:
C++之std::atomic解决多线程7个问题(二百四)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...
tailwindcss 如何在 uniapp 中使用
直接使用https://tailwindcss.com/docs/guides/vite这篇官方教程的写法是跑不通的,摸索以后整理了一下,最关键的是第6步 npm install -D tailwindcss postcss autoprefixernpx tailwindcss init -p在 tailwind.config.js 中写入 export default {conten…...
oracle-使用PLSQL工具自行修改用户密码
1、使用PLSQL工具,输入用户名和原密码登录,如下图 2、登录后,在会话下拉菜单中找到”Change password..” 3、在跳出的窗口中配置新密码,修改完成后单击”确认”,后退出PLSQL 4、重新打开PLSQL,使用新密码登…...
自动驾驶技术:现状与未来
自动驾驶技术:现状与未来 文章目录 引言自动驾驶技术的现状自动驾驶技术的挑战自动驾驶技术的未来结论结论 2023星火培训【专项营】Apollo开发者社区布道师倾力打造,包含PnC、新感知等的全新专项课程上线了。理论与实践相结合,全新的PnC培训不…...
C++ 类构造函数 析构函数
类的构造函数 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。 下面的实例有助于更好地…...
C++标准模板(STL)- 输入/输出操纵符-(std::get_time,std::put_time)
操纵符是令代码能以 operator<< 或 operator>> 控制输入/输出流的帮助函数。 不以参数调用的操纵符(例如 std::cout << std::boolalpha; 或 std::cin >> std::hex; )实现为接受到流的引用为其唯一参数的函数。 basic_ostream::…...
蓝桥等考Python组别九级004
第一部分:选择题 1、Python L9 (15分) 运行下面程序,可以输出几行“*”?( ) for i in range(3): for j in range(4): print(*, end = ) print() 2345正确答案:B 2、Python L9...
gitee 远程仓库操作基础(二)
(1)clone远端仓库,本地建立分支推送 (基于远程仓库版本库 本地建立分支开发新功能) git clone gitgitee.com:xxxxx/alsa_test.git git remote add origin gitgitee.com:xxxxx/alsa_test.git进入clone过后路径代码,查看本地分支,发现该项目远程仓库有很多分支 基于…...
Scala第四章节
Scala第四章节 scala总目录 章节目标 掌握分支结构的格式和用法掌握for循环和while循环的格式和用法掌握控制跳转语句的用法掌握循环案例理解do.while循环的格式和用法 1. 流程控制结构 1.1 概述 在实际开发中, 我们要编写成千上万行代码, 代码的顺序不同, 执行结果肯定也…...
【C++入门指南】类和对象(上)
【C杂货店】类和对象(上) 一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装4.1 访问限定符4.2 封装 五、类的作用域六、类的实例化七、类对象模型7.1 类对象的存储规则7.2 例题7.3结构体内存对齐规则 八、this指针8.2 t…...
web:[极客大挑战 2019]PHP
题目 点进页面显示如下 根据页面提示,这个网站有备份文件,备份文件一般是bak文件格式,用dirsearch扫描 访问之后下载了一个文件 里面都是一些代码 在index.php中发现了一个类的文件,一个get传参,然后将传进的值进行反序…...
Firefox 开发团队对 Vue 3 进行优化效果显著
Mozilla 官方博客近日发表文章《Faster Vue.js Execution in Firefox》,介绍了 Firefox 开发团队对 Vue 3 进行的优化。 文章写道,在使用 Speedometer 3 对 Firefox 进行基准测试时,他们发现 Vue.js test 的测试结果从 Vue 2 升级到 Vue 3 后…...
【Verilog 教程】6.5 Verilog避免Latch
关键词:触发器,锁存器 Latch 的含义 锁存器(Latch),是电平触发的存储单元,数据存储的动作取决于输入时钟(或者使能)信号的电平值。仅当锁存器处于使能状态时,输出才会随着…...
怒刷LeetCode的第21天(Java版)
目录 第一题 题目来源 题目内容 解决方法 方法一:哈希表 方法二:计数器数组 第二题 题目来源 题目内容 解决方法 方法一:分治法 方法二:快速幂 迭代 方法三:快速幂 递归 第三题 题目来源 题目内容 …...
Armv9 Cortex-A720的L2 memory system 和 L2 Cache
9 L2 memory system Cortex-A720核心的L2内存系统通过CPU bridge连接core与DynamIQ Shared Unit-120,其中包括私有的L2缓存。 L2缓存是统一的,每个Cortex-A720核心在一个集群中都有私有的L2缓存。 L2内存系统包括使用虚拟地址(VA)和程序计数器(PC)的数据预取引擎。不同…...
蓝桥等考Python组别九级003
第一部分:选择题 1、Python L9 (15分) 运行下面程序,可以输出几行“*”?( ) for i in range(3): for j in range(4): print(*, end = ) print() 6374正确答案:B 2、Python L9...
Python异步框架大战:FastAPI、Sanic、Tornado VS Go 的 Gin
一、前言 异步编程在构建高性能 Web 应用中起着关键作用,而 FastAPI、Sanic、Tornado 都声称具有卓越的性能。本文将通过性能压测对这些框架与Go的Gin框架进行全面对比,揭示它们之间的差异。 原文:Python异步框架大战:FastAPI、Sa…...
Docker笔记1
一、Docker介绍 Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。 容器是完全使用沙箱机制&a…...
TensorFlow-Federated简介与安装
1、简介 TensorFlow Federated(TFF)是一个用于机器学习和其他分布式数据计算的开源框架。TFF 的开发旨在促进联邦学习 (FL)的开放研究和实验。联邦学习是一种机器学习方法,其中一个共享的全局模型在许多参与的客户之间…...
【强化学习】基础概念
1. Agent (智能体) 智能体是进行决策和学习的实体,它能感知环境的状态,并基于策略采取动作以影响环境。智能体的目标是通过与环境的交互获得最大化的累积奖励。 2. Environment (环境) 环境是智能体所处的外部系统,它与智能体交互。环境的…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
