RAII 与 std::lock_guard 在 C++ 中的应用:自动化互斥锁管理与线程安全
目录
1. RAII(资源获取即初始化)概述
RAII 的优点
2. std::lock_guard 的工作原理
2.1 构造函数
2.2 析构函数
2.3 关键特性
3. 为什么 std::lock_guard 能自动管理锁的生命周期
3.1 RAII 原则的应用
3.2 异常安全
3.3 简化代码和减少错误
4. 代码示例分析
示例代码
执行流程
输出示例
5. std::lock_guard 的替代方案
5.1 std::unique_lock
5.2 std::shared_lock
6. 总结
std::lock_guard<std::mutex>
能够自动管理锁的生命周期,主要得益于 RAII(资源获取即初始化) 这一编程范式。
1. RAII(资源获取即初始化)概述
RAII 是一种常用的编程技术,尤其在 C++ 中广泛应用。其核心思想是将资源的获取和释放绑定到对象的生命周期上,即:
- 资源获取:在对象的构造函数中获取资源。
- 资源释放:在对象的析构函数中释放资源。
通过这种方式,可以确保资源在对象生命周期内被正确管理,避免资源泄漏和其他管理错误。
RAII 的优点
- 自动管理资源:无需手动调用释放资源的函数,减少人为错误。
- 异常安全:即使在异常发生时,析构函数也会被调用,确保资源被释放。
- 简化代码:减少显式的资源管理代码,使代码更加简洁和易读。
2. std::lock_guard
的工作原理
std::lock_guard<std::mutex>
是一个遵循 RAII 原则的类模板,用于管理互斥锁(std::mutex
)的生命周期。它的设计确保了锁在对象的整个生命周期内被持有,并在对象销毁时自动释放锁。
2.1 构造函数
当创建一个 std::lock_guard<std::mutex>
对象时,其构造函数会自动调用互斥锁的 lock()
方法,获取锁。
std::mutex mtx;void example() {std::lock_guard<std::mutex> lock(mtx); // 构造时自动调用 mtx.lock()// 临界区代码
} // lock 对象析构时自动调用 mtx.unlock()
2.2 析构函数
当 std::lock_guard<std::mutex>
对象超出其作用域时,其析构函数会自动调用互斥锁的 unlock()
方法,释放锁。
{std::lock_guard<std::mutex> lock(mtx); // 获取锁// 临界区代码
} // lock 对象析构,自动释放锁
2.3 关键特性
-
不可复制和不可移动:
std::lock_guard
禁止复制和移动,以确保锁的唯一所有权,防止多个lock_guard
对象管理同一把锁。std::lock_guard<std::mutex> lock1(mtx); // std::lock_guard<std::mutex> lock2 = lock1; // 编译错误
-
轻量级:
std::lock_guard
本身不占用过多资源,主要负责锁的获取和释放。
3. 为什么 std::lock_guard
能自动管理锁的生命周期
3.1 RAII 原则的应用
std::lock_guard
遵循 RAII 原则,通过其构造函数和析构函数自动管理锁的获取和释放:
- 构造阶段:当
lock_guard
对象被创建时,自动获取锁。 - 析构阶段:当
lock_guard
对象被销毁时,自动释放锁。
这种设计使得锁的管理与对象的生命周期紧密绑定,无需手动干预。
3.2 异常安全
在临界区代码中,如果发生异常,lock_guard
的析构函数仍会被调用,确保锁被正确释放,防止死锁。
void example() {std::lock_guard<std::mutex> lock(mtx); // 获取锁// 临界区代码throw std::runtime_error("Error occurred"); // 异常发生
} // lock 对象析构,自动释放锁
上述代码中,即使在临界区抛出异常,lock_guard
仍会在栈展开过程中被销毁,自动调用 mtx.unlock()
,释放锁。
3.3 简化代码和减少错误
使用 std::lock_guard
可以显著简化代码,避免手动调用 lock()
和 unlock()
可能导致的错误,如忘记释放锁、异常情况下未释放锁等。
手动管理锁的风险:
void risky_example() {mtx.lock();// 临界区代码if (some_condition) {mtx.unlock(); // 可能被遗漏return;}// 更多代码mtx.unlock(); // 可能未被执行
}
使用 std::lock_guard
的安全性:
void safe_example() {std::lock_guard<std::mutex> lock(mtx); // 自动获取锁// 临界区代码if (some_condition) {return; // 自动释放锁}// 更多代码
} // 自动释放锁
4. 代码示例分析
让我们通过一个具体的代码示例,进一步理解 std::lock_guard
如何自动管理锁的生命周期。
示例代码
#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void print_thread_id(int id) {std::lock_guard<std::mutex> lock(mtx); // 构造时获取锁std::cout << "Thread " << id << " is running.\n";// 析构时自动释放锁
}int main() {std::thread t1(print_thread_id, 1);std::thread t2(print_thread_id, 2);t1.join();t2.join();return 0;
}
执行流程
-
创建
lock_guard
对象:- 当
lock
对象被创建时,调用mtx.lock()
,获取锁。
- 当
-
执行临界区代码:
- 输出线程 ID,确保输出操作是线程安全的,不会被其他线程打断。
-
析构
lock_guard
对象:- 当
lock
对象超出作用域(函数返回或异常发生时),调用mtx.unlock()
,释放锁。
- 当
输出示例
Thread 1 is running.
Thread 2 is running.
确保每个线程在输出时都持有锁,避免输出内容交错。
5. std::lock_guard
的替代方案
虽然 std::lock_guard
是管理锁的简单而高效的方式,但在某些情况下,其他锁管理工具可能更适合:
5.1 std::unique_lock
- 更灵活:允许手动锁定和解锁,可以延长锁的持有时间。
- 支持延迟锁定:可以在构造时不立即获取锁。
- 适用于需要更复杂锁管理的场景。
示例:
std::mutex mtx;void example() {std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 不立即获取锁// 其他操作lock.lock(); // 手动获取锁// 临界区代码// lock 会在析构时自动释放锁
}
5.2 std::shared_lock
- 适用于共享锁:允许多个线程同时读取共享资源,但写操作需要独占锁。
- 与
std::shared_mutex
搭配使用。
示例:
#include <shared_mutex>std::shared_mutex shared_mtx;void read_operation() {std::shared_lock<std::shared_mutex> lock(shared_mtx); // 共享锁// 读取共享资源
}void write_operation() {std::unique_lock<std::shared_mutex> lock(shared_mtx); // 独占锁// 写入共享资源
}
6. 总结
std::lock_guard<std::mutex>
通过 RAII 原则,利用构造和析构函数自动管理锁的获取和释放,确保了线程安全性并简化了代码。其主要优势包括:
- 自动锁管理:减少手动锁定和释放的错误风险。
- 异常安全:即使在异常情况下,锁也能被正确释放。
- 代码简洁:使用
lock_guard
可以让代码更加清晰和易维护。
通过遵循 RAII 原则,std::lock_guard
为多线程编程提供了一种高效、安全且易于使用的锁管理方式,是现代 C++ 中推荐的锁管理工具。
相关文章:
RAII 与 std::lock_guard 在 C++ 中的应用:自动化互斥锁管理与线程安全
目录 1. RAII(资源获取即初始化)概述 RAII 的优点 2. std::lock_guard 的工作原理 2.1 构造函数 2.2 析构函数 2.3 关键特性 3. 为什么 std::lock_guard 能自动管理锁的生命周期 3.1 RAII 原则的应用 3.2 异常安全 3.3 简化代码和减少错误 4.…...

风格汇:奢华风格在UI设计中如何被定义的。
在UI设计中,奢华风格通常指的是一种高端、豪华、精致的设计风格,旨在营造出奢华、豪华的视觉效果,给用户带来高品质、高档次的感受。 奢华风格的UI设计通常会运用一些富丽堂皇的元素和效果,例如金色、银色、贵族紫、华丽的字体、华…...

Vue2 qrcode+html2canvas 实现二维码的生成和保存
1.安装 npm install qrcode npm install html2canvas 2.引用 import QRCode from qrcode import html2canvas from html2canvas 效果: 1. 二维码生成: 下载二维码图片: 二维码的内容: 实现代码: <template>…...
GEE 教程:利用Google Dynamic数据进行逐月指定区域的土地分类数据提取分析
目录 简介 数据 代码 结果 简介 利用Google Dynamic数据进行逐月指定区域的土地分类数据提取分析 数据 Google Dynamic数据是指由Google自动生成、自动更新的数据,它不需要人工干预,而是通过算法和机器学习技术从各种来源获取并解析数据。这些数据可以是来自互联网上的…...
Nginx 负载均衡:优化网站性能与可扩展性的利器
在当今高流量的互联网时代,网站的性能和可扩展性成为了衡量其成功与否的关键因素之一。随着用户量的不断增加,单一服务器往往难以承受巨大的访问压力,这时就需要引入负载均衡技术来分散请求,提高系统的整体性能和可靠性。Nginx&am…...

【Python基础】Python错误和异常处理(详细实例)
本文收录于 《Python编程入门》专栏,从零基础开始,分享一些Python编程基础知识,欢迎关注,谢谢! 文章目录 一、前言二、Python中的错误类型三、Python异常处理机制3.1 try-except语句3.2 try-except-else语句3.3 try-fi…...

如何查看串口被哪个程序占用?截止目前最方便的方法
痛点:串口因为某种原因被占用,如何找到罪魁祸首? 做开发的小伙伴们,经常会遇到这样的问题:串口因为某种原因被占用,导致无法通讯,但是又找不到被哪个程序占用。只有重启电脑,才能解…...

深入理解SpringBoot(一)----SpringBoot的启动流程分析
1、SpringApplication 对象实例化 SpringApplication 文件 public static ConfigurableApplicationContext run(Object[] sources, String[] args) {// 传递的source其实就是类Bootstrapreturn new SpringApplication(sources).run(args);// 实例化一个SpringApplication对象执…...

MySql基础-单表操作
1. MYSQL概述 1.1 数据模型 关系型数据库 关系型数据库(RDBMS):建立在关系模型基础上,由多张相互连接的二维表组成的数据库。 特点: 使用表存储数据,格式统一,便于维护 使用SQL语言操作,标准统一&…...

【STM32系统】基于STM32设计的SD卡数据读取与上位机显示系统(SDIO接口驱动、雷龙SD卡)——文末资料下载
基于STM32设计的SD卡数据读取与上位机显示系统 演示视频: 基于STM32设计的SD卡数据读取与上位机显示系统 简介:本研究的主要目的是基于STM32F103微控制器,设计一个能够读取SD卡数据并显示到上位机的系统。SD卡的数据扇区读取不仅是为了验证存…...
SpringBoot开发——整合Redis
文章目录 1、创建项目,添加Redis依赖2、创建实体类Student3、创建Controller4、配置application.yml5、整合完成 Redis ( Remote Dictionary Server )是一个开源的内存数据库,遵守 BSD 协议,它提供了一个高性能的键值(…...
OpenCV结构分析与形状描述符(17)判断轮廓是否为凸多边形的函数isContourConvex()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 测试轮廓的凸性。 该函数测试输入的轮廓是否为凸的。轮廓必须是简单的,即没有自相交。否则,函数的输出是不确定的。 cv:…...
P5425 [USACO19OPEN] I Would Walk 500 Miles G
*原题链接* 很离谱的题。首先可以想到暴力连边,整个图为一个完全图,将所有的边选出来,然后从小到大一条条加入,当剩下集合数量 <K 的时候就结束。答案为加入的最后一条边的大小。如果用prim算法的话时间复杂度为。足以通过此题…...

Java高级Day41-反射入门
115.反射 反射机制 1.根据配置文件re.properties指定信息,创建Cat对象并调用hi方法 SuppressWarnings({"all"}) public class ReflectionQuestion {public static void main(String[] args) throws IOException {//根据配置文件 re.properties 指定信息…...
在Linux系统上使用Docker部署java项目
一.使用Docker部署的好处: 在Linux系统上使用Docker部署项目通常会大大简化部署流程,因为Docker可以将应用程序及其依赖打包到一个独立的容器中。 Docker打包应用程序时会将其与所有依赖项(操作系统、库等)一起打包。这样&#…...

【C++】标准库IO查漏补缺
【C】标准库 IO 查漏补缺 文章目录 系统I/O1. 概述2. cout 与 cerr3. cerr 和 clog4. 缓冲区5. 与 printf 的比较 系统I/O 1. 概述 标准库提供的 IO 接口,包含在 iostream 文件中 输入流: cin输出流:cout / cerr / clog。 输入流只有一个 cin&#x…...
python简单易懂的lxml读取HTML节点及常用操作方法
python简单易懂的lxml读取HTML节点及常用操作方法 1. 初始化和基本概念 lxml 是一个强大的pyth库,用于处理XML和HTML文档。它提供了类似BeautifulSoup的功能,但性能更高。在使用lxml时,通常会先解析HTML或XML文档,得到一个Eleme…...

Java | Leetcode Java题解之第406题根据身高重建队列
题目: 题解: class Solution {public int[][] reconstructQueue(int[][] people) {Arrays.sort(people, new Comparator<int[]>() {public int compare(int[] person1, int[] person2) {if (person1[0] ! person2[0]) {return person2[0] - perso…...

安卓获取apk的公钥,用于申请app备案等
要申请app的icp备案等场景,需要app的 证书MD5指纹和公钥,示例如下: 步骤1:使用keytool从APK中提取证书 1. 打开命令行,cd 到你的apk目录,如:app/release 2. 解压APK文件: unzip yo…...

【leetcode_python】杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRows 1 输出: [[1]] 方案&#…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...