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 (环境) 环境是智能体所处的外部系统,它与智能体交互。环境的…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...
热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
在Zenodo下载文件 用到googlecolab googledrive
方法:Figshare/Zenodo上的数据/文件下载不下来?尝试利用Google Colab :https://zhuanlan.zhihu.com/p/1898503078782674027 参考: 通过Colab&谷歌云下载Figshare数据,超级实用!!࿰…...
