当前位置: 首页 > news >正文

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 的优点

  1. 自动管理资源:无需手动调用释放资源的函数,减少人为错误。
  2. 异常安全:即使在异常发生时,析构函数也会被调用,确保资源被释放。
  3. 简化代码:减少显式的资源管理代码,使代码更加简洁和易读。

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

执行流程

  1. 创建 lock_guard 对象

    • lock 对象被创建时,调用 mtx.lock(),获取锁。
  2. 执行临界区代码

    • 输出线程 ID,确保输出操作是线程安全的,不会被其他线程打断。
  3. 析构 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 原则,利用构造和析构函数自动管理锁的获取和释放,确保了线程安全性并简化了代码。其主要优势包括:

  1. 自动锁管理:减少手动锁定和释放的错误风险。
  2. 异常安全:即使在异常情况下,锁也能被正确释放。
  3. 代码简洁:使用 lock_guard 可以让代码更加清晰和易维护。

通过遵循 RAII 原则,std::lock_guard 为多线程编程提供了一种高效、安全且易于使用的锁管理方式,是现代 C++ 中推荐的锁管理工具。

 

相关文章:

RAII 与 std::lock_guard 在 C++ 中的应用:自动化互斥锁管理与线程安全

目录 1. RAII&#xff08;资源获取即初始化&#xff09;概述 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设计中&#xff0c;奢华风格通常指的是一种高端、豪华、精致的设计风格&#xff0c;旨在营造出奢华、豪华的视觉效果&#xff0c;给用户带来高品质、高档次的感受。 奢华风格的UI设计通常会运用一些富丽堂皇的元素和效果&#xff0c;例如金色、银色、贵族紫、华丽的字体、华…...

Vue2 qrcode+html2canvas 实现二维码的生成和保存

1.安装 npm install qrcode npm install html2canvas 2.引用 import QRCode from qrcode import html2canvas from html2canvas 效果&#xff1a; 1. 二维码生成&#xff1a; 下载二维码图片&#xff1a; 二维码的内容&#xff1a; 实现代码&#xff1a; <template>…...

GEE 教程:利用Google Dynamic数据进行逐月指定区域的土地分类数据提取分析

目录 简介 数据 代码 结果 简介 利用Google Dynamic数据进行逐月指定区域的土地分类数据提取分析 数据 Google Dynamic数据是指由Google自动生成、自动更新的数据,它不需要人工干预,而是通过算法和机器学习技术从各种来源获取并解析数据。这些数据可以是来自互联网上的…...

Nginx 负载均衡:优化网站性能与可扩展性的利器

在当今高流量的互联网时代&#xff0c;网站的性能和可扩展性成为了衡量其成功与否的关键因素之一。随着用户量的不断增加&#xff0c;单一服务器往往难以承受巨大的访问压力&#xff0c;这时就需要引入负载均衡技术来分散请求&#xff0c;提高系统的整体性能和可靠性。Nginx&am…...

【Python基础】Python错误和异常处理(详细实例)

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、Python中的错误类型三、Python异常处理机制3.1 try-except语句3.2 try-except-else语句3.3 try-fi…...

如何查看串口被哪个程序占用?截止目前最方便的方法

痛点&#xff1a;串口因为某种原因被占用&#xff0c;如何找到罪魁祸首&#xff1f; 做开发的小伙伴们&#xff0c;经常会遇到这样的问题&#xff1a;串口因为某种原因被占用&#xff0c;导致无法通讯&#xff0c;但是又找不到被哪个程序占用。只有重启电脑&#xff0c;才能解…...

深入理解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)&#xff1a;建立在关系模型基础上&#xff0c;由多张相互连接的二维表组成的数据库。 特点&#xff1a; 使用表存储数据&#xff0c;格式统一&#xff0c;便于维护 使用SQL语言操作&#xff0c;标准统一&…...

【STM32系统】基于STM32设计的SD卡数据读取与上位机显示系统(SDIO接口驱动、雷龙SD卡)——文末资料下载

基于STM32设计的SD卡数据读取与上位机显示系统 演示视频&#xff1a; 基于STM32设计的SD卡数据读取与上位机显示系统 简介&#xff1a;本研究的主要目的是基于STM32F103微控制器&#xff0c;设计一个能够读取SD卡数据并显示到上位机的系统。SD卡的数据扇区读取不仅是为了验证存…...

SpringBoot开发——整合Redis

文章目录 1、创建项目&#xff0c;添加Redis依赖2、创建实体类Student3、创建Controller4、配置application.yml5、整合完成 Redis ( Remote Dictionary Server &#xff09;是一个开源的内存数据库&#xff0c;遵守 BSD 协议&#xff0c;它提供了一个高性能的键值&#xff08…...

OpenCV结构分析与形状描述符(17)判断轮廓是否为凸多边形的函数isContourConvex()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 测试轮廓的凸性。 该函数测试输入的轮廓是否为凸的。轮廓必须是简单的&#xff0c;即没有自相交。否则&#xff0c;函数的输出是不确定的。 cv:…...

P5425 [USACO19OPEN] I Would Walk 500 Miles G

*原题链接* 很离谱的题。首先可以想到暴力连边&#xff0c;整个图为一个完全图&#xff0c;将所有的边选出来&#xff0c;然后从小到大一条条加入&#xff0c;当剩下集合数量 <K 的时候就结束。答案为加入的最后一条边的大小。如果用prim算法的话时间复杂度为。足以通过此题…...

Java高级Day41-反射入门

115.反射 反射机制 1.根据配置文件re.properties指定信息&#xff0c;创建Cat对象并调用hi方法 SuppressWarnings({"all"}) public class ReflectionQuestion {public static void main(String[] args) throws IOException {//根据配置文件 re.properties 指定信息…...

在Linux系统上使用Docker部署java项目

一.使用Docker部署的好处&#xff1a; 在Linux系统上使用Docker部署项目通常会大大简化部署流程&#xff0c;因为Docker可以将应用程序及其依赖打包到一个独立的容器中。 Docker打包应用程序时会将其与所有依赖项&#xff08;操作系统、库等&#xff09;一起打包。这样&#…...

【C++】标准库IO查漏补缺

【C】标准库 IO 查漏补缺 文章目录 系统I/O1. 概述2. cout 与 cerr3. cerr 和 clog4. 缓冲区5. 与 printf 的比较 系统I/O 1. 概述 标准库提供的 IO 接口&#xff0c;包含在 iostream 文件中 输入流: cin输出流&#xff1a;cout / cerr / clog。 输入流只有一个 cin&#x…...

python简单易懂的lxml读取HTML节点及常用操作方法

python简单易懂的lxml读取HTML节点及常用操作方法 1. 初始化和基本概念 lxml 是一个强大的pyth库&#xff0c;用于处理XML和HTML文档。它提供了类似BeautifulSoup的功能&#xff0c;但性能更高。在使用lxml时&#xff0c;通常会先解析HTML或XML文档&#xff0c;得到一个Eleme…...

Java | Leetcode Java题解之第406题根据身高重建队列

题目&#xff1a; 题解&#xff1a; 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备案等场景&#xff0c;需要app的 证书MD5指纹和公钥&#xff0c;示例如下&#xff1a; 步骤1&#xff1a;使用keytool从APK中提取证书 1. 打开命令行&#xff0c;cd 到你的apk目录&#xff0c;如&#xff1a;app/release 2. 解压APK文件&#xff1a; unzip yo…...

【leetcode_python】杨辉三角

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

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...