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

C++11特性:std::lock_guard是否会引起死锁?

今天在评审代码的时候,因为位于两个不同的线程中(一个是周期性事件线程,一个是触发式事件线程),需要对一个资源类的某些属性进行互斥的访问,因此采用lock_guard互斥量包装器,但是在升级的过程中,因为整个系统太大,所以在询问了某位同事后,得到的答案是在两个不同的地方加上lock_guard有一定的可能性会导致死锁,但是后面在测试的过程中又没有问题,真的如此吗?本文针对lock_guard来做阐述和延申

历史原因:为什么要使用lock_gurad,有什么优点

传统的C++对互斥量加锁是用互斥量本身的锁的,即

#include <mutex>std::mutex g_mutex;// 加锁
g_mutex.lock();
// 解锁
g_mutex.unlock();

此时会有两个不便利处,即要手动的解锁,如果忘了解锁,那就完蛋了!,因此采用lock_guard

#include <mutex>std::mutex g_mutex;{ // 作用域开始std::lock_guard<std::mutex> lock(g_mutex);
} // 作用域结束

lock_guard以作用域为加解锁单位,退出当前作用域自动解锁,不必手动解锁。总结一下它的优点

  • RAII(资源获取即初始化)语法:std::lock_guard 使用对象的构造函数和析构函数来自动管理互斥量的锁定和解锁,从而避免了手动管理锁的复杂性。这种自动化的资源管理方式确保在作用域结束时释放锁,防止忘记解锁而导致的资源泄漏或死锁
  • 异常安全:由于 std::lock_guard 使用RAII语法,即使在作用域内发生异常,也会自动调用析构函数释放锁,确保了异常安全性。这样可以避免在异常发生时锁没有被释放而导致的资源泄漏或死锁
  • 简单易用:std::lock_guard 提供了一种简单而直观的方式来管理互斥量,不需要手动调用 lock() unlock() 函数,因此代码更加简洁清晰,易于理解和维护
  • 避免死锁:由于 std::lock_guard 在构造时立即锁定互斥量,在析构时立即释放互斥量,因此减少了出现死锁的可能性。通过使用 std::lock_guard,程序员可以更容易地确保正确的加锁和解锁顺序,从而避免死锁
  • 线程安全性:std::lock_guard 是线程安全的,可以在多线程环境下安全地使用。它能够确保在同一时间只有一个线程可以访问被互斥量保护的资源,从而保证了线程安全性

lock_guard和unique_lock的区别

延迟加锁:可以在构造时不立即锁定互斥量,在需要时手动调用 lock() 函数来锁定互斥量。这种延迟加锁的特性允许在一段代码中多次锁定和解锁互斥量,提供了更多的灵活性

void FunThread()
{std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟加锁,此时不加锁lock.lock(); // 加锁//lock.unlock(); // 解锁
}

条件等待:提供了与条件变量一起使用的功能,可以在条件变量的等待和通知过程中自动锁定和解锁互斥量。通过将 std::unique_lock 对象传递给条件变量的wait()notify_*() 函数,可以轻松实现条件等待的功能

配合条件变量使用的时候必须使用unique_lock,因此lock_guard不会自动释放锁,试想,在wait之前,本线程已经持有锁了,在等待的时候,如果一直持有互斥量,那其他线程也会拿不到互斥量,执行不了逻辑,则会导致永久死锁,可见源码

    template <class _Lock, class _Predicate>bool wait(_Lock& __lock,stop_token __stoken,_Predicate __p){if (__stoken.stop_requested()){return __p();}std::stop_callback __cb(__stoken, [this] { notify_all(); });shared_ptr<mutex> __mutex = _M_mutex;while (!__p()){unique_lock<mutex> __my_lock(*__mutex);if (__stoken.stop_requested()){return false;}// *__mutex must be unlocked before re-locking __lock so move// ownership of *__mutex lock to an object with shorter lifetime._Unlock<_Lock> __unlock(__lock); // 先解锁unique_lock<mutex> __my_lock2(std::move(__my_lock));_M_cond.wait(__my_lock2);}return true;}
{std::unique_lock<std::mutex> lock(g_mutex);g_condition.wait(lock, [] { // condition }); // 等待队列不为空的才去获得锁
}

所有权转移:支持所有权转移,允许将互斥量的所有权从一个std::unique_lock对象转移到另一个对象。这使得在一段代码中传递互斥量的所有权变得更加方便

void Fun2(std::unique_lock<std::mutex> lock)
{// 手动解锁锁lock.unlock();
}
void Fun1()
{std::unique_lock<std::mutex> lock(mtx);std::thread F(Fun2, std::move(lock));
}

性能损失:与 std::lock_guard 相比,std::unique_lock 有一些性能损失,因为它需要更多的控制和管理互斥量的状态,包括手动加锁、解锁和条件等待。但是,这种性能损失通常是微不足道的,并且可以通过使用std::defer_lock 参数来避免

还有一个scope_lock,在此不做介绍

lock_guard小demo:读写队列

/** @Author: LiuHao* @Date: 2024-03-24 01:40:15* @Description: 测试C++的互斥量包装器*/#include <thread>
#include <mutex>
#include <iostream>
#include <deque>
#include <vector>
#include <condition_variable>std::mutex g_mutex; // 互斥量
std::deque<uint8_t> g_deque; // 双端队列
std::condition_variable g_condition; // 条件变量namespace LHSpace { // 解决cout不能直接输出uint8_t的值的问题class LHCout {public:template <typename T>typename std::enable_if<std::is_same<T, uint8_t>::value, LHCout&>::type operator<<(const T& num){std::cout << static_cast<int>(num);return *this;}LHCout& operator<<(std::ostream& (*manipulator)(std::ostream&)){std::cout << manipulator;return *this;}};
}

demo组成

  • 十个写线程
  • 一个循环读线程

写线程函数

void SetPrint(const uint8_t val)
{LHSpace::LHCout lhCout;{std::lock_guard<std::mutex> lock(g_mutex);std::cout << "Write Thread ID: " << std::this_thread::get_id() << std::endl;g_deque.push_back(val); // 末尾添加一个元素std::cout << "deque push: ";lhCout << val << std::endl;}g_condition.notify_one(); // 使用条件变量std::this_thread::sleep_for(std::chrono::seconds(1));
}

读线程函数

void GetPrint()
{LHSpace::LHCout lhCout;while (true) { // 每一次退出作用域就丢掉锁std::unique_lock<std::mutex> lock(g_mutex);g_condition.wait(lock, [] { return !g_deque.empty(); }); // 等待队列不为空的才去获得锁const uint8_t FRONTNUM = g_deque.front();g_deque.pop_front();lock.unlock();std::cout << "Read Thread ID: " << std::this_thread::get_id() << std::endl;std::cout << "deque pop: ";lhCout << FRONTNUM << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(500));}
}

创建并启动线程

int main(int argc, const char** argv)
{LHSpace::LHCout lhCout;std::vector<std::thread> writeThreads;for (uint8_t i = 0U; i < 10U; i++) {writeThreads.push_back(std::thread(SetPrint, i)); // 读线程}std::thread getThread(GetPrint); // 读线程for (uint8_t i = 0U; i < 10U; i++) {writeThreads[i].join();}getThread.join();return 0;
}

来看看具体运行过程,感受一下多线程的魅力

ubuntu@VM-8-16-ubuntu:~/tmp$ ./a.out 
Write Thread ID: 140363031643712
deque push: 2 # 写
Write Thread ID: 140363023251008
deque push: 3 # 写
Read Thread ID: 140362964502080
deque pop: 2 # 读
Write Thread ID: 140363040036416
deque push: 1 # 写
Write Thread ID: 140363048429120
deque push: 0 # 写
Write Thread ID: 140363014858304
deque push: 4 # 写
Read Thread ID: 140362964502080
deque pop: 3 # 读
# ...

相关文章:

C++11特性:std::lock_guard是否会引起死锁?

今天在评审代码的时候&#xff0c;因为位于两个不同的线程中&#xff08;一个是周期性事件线程&#xff0c;一个是触发式事件线程&#xff09;&#xff0c;需要对一个资源类的某些属性进行互斥的访问&#xff0c;因此采用lock_guard互斥量包装器&#xff0c;但是在升级的过程中…...

stm32使用定时器实现PWM与呼吸灯

PWM介绍 STM32F103C8T6 PWM 资源&#xff1a; 高级定时器&#xff08; TIM1 &#xff09;&#xff1a; 7 路 通用定时器&#xff08; TIM2~TIM4 &#xff09;&#xff1a;各 4 路 例如定时器2 PWM 输出模式&#xff1a; PWM 模式 1 &#xff1a;在 向上计数 时&#xff0…...

MAC本安装telnet

Linux运维工具-ywtool 目录 1.打开终端1.先安装brew命令2.写入环境变量4.安装telnet 1.打开终端 访达 - 应用程序(左侧) - 实用工具(右侧) - 终端 #注意:登入终端用普通用户,不要用MAC的root用户1.先安装brew命令 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/H…...

[AIGC] 使用Spring Boot进行单元测试:一份指南

在现代软件开发过程中&#xff0c;确认你的应用正确运行是至关重要的一步。Spring Boot提供了一组实用工具和注解来辅助你在测试你的应用时&#xff0c;使得这个过程变得简单。下面就来分享一下如何在Spring Boot中进行单元测试。 文章目录 为什么需要单元测试Spring Boot单元测…...

使用 Go 语言统计 0-200000 的数字中,哪些是素数?

题目 使用 Go 语言统计 0-200000的数字中&#xff0c;哪些是素数&#xff1f; 思路 两种方法&#xff1a; 单循环遍历 1-200000 数字&#xff0c;并判断是否是素数。 使用了 Goroutine 和通道实现并发&#xff1a; 通过创建两个通道 intChan 和 primeChan&#xff0c;以及一…...

Fabric Measurement

Fabric Measurement 布料测量...

wayland(xdg_wm_base) + egl + opengles 使用 Assimp 加载材质文件Mtl 中的纹理图片最简实例(十六)

文章目录 前言一、3d 立方体 model 属性相关文件1. cube.obj2. cube.Mtl3. 纹理图片 cordeBouee4.jpg二、代码实例1. 依赖库和头文件1.1 assimp1.2 stb_image.h2. egl_wayland_obj_cube.cpp3. Matrix.h 和 Matrix.cpp4. xdg-shell-client-protocol.h 和 xdg-shell-protocol.c5.…...

面试常问:为什么 Vite 速度比 Webpack 快?

前言 最近作者在学习 webpack 相关的知识&#xff0c;之前一直对这个问题不是特别了解&#xff0c;甚至讲不出个123....&#xff0c;这个问题在面试中也是常见的&#xff0c;作者在学习的过程当中总结了以下几点&#xff0c;在这里分享给大家看一下&#xff0c;当然最重要的是…...

React腳手架已經創建好了,想使用Vite作為開發依賴

使用Vite作為開發依賴 安裝VITE配置VITE配置文件簡單的VITE配置項更改package.json中的scripts在根目錄中添加index.html現在可以瀏覽你的頁面了 安裝VITE 首先&#xff0c;在現有的React項目中安裝VITE npm install vite --save-dev || yarn add vite --dev配置VITE配置文件 …...

数据结构——双向链表(C语言版)

上一章&#xff1a;数据结构——单向链表&#xff08;C语言版&#xff09;-CSDN博客 目录 什么是双向链表&#xff1f; 双向链表的节点结构 双向链表的基本操作 完整的双向链表示例 总结 什么是双向链表&#xff1f; 双向链表是一种常见的数据结构&#xff0c;它由一系列节…...

缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题

一、缓存雪崩 简单理解&#xff1a;由于原有缓存失效&#xff0c;新缓存未到期间 (例如&#xff1a;设置缓存时采用了相同的过期时间&#xff0c;在同一时刻出现大面积的缓存过期)&#xff0c;所有原本应该访问缓存的请求都去查询数据库了&#xff0c;而对数据库CPU和内存造成…...

深度学习pytorch——多层感知机反向传播(持续更新)

在讲解多层感知机反向传播之前&#xff0c;先来回顾一下多输出感知机的问题&#xff0c;下图是一个多输出感知机模型&#xff1a; 课时44 反向传播算法-1_哔哩哔哩_bilibili 根据上一次的分析深度学习pytorch——感知机&#xff08;Perceptron&#xff09;&#xff08;持续更新…...

五、分布式锁-redission

源码仓库地址&#xff1a;gitgitee.com:chuangchuang-liu/hm-dingping.git 1、redission介绍 目前基于redis的setnx特性实现的自定义分布式锁仍存在的问题&#xff1a; 问题描述重入问题同一个线程无法多次获取统一把锁。当方法A成功获取锁后&#xff0c;调用方法B&#xff0…...

ARM的三个按键实验

main.c #include "key_inc.h"//封装延时函数void delay(int ms){int i,j;for(i0;i<ms;i){for(j0;j<2000;j){}}}int main(){//按键中断初始化key1_it_config();key2_it_config();key3_it_config();while(1){printf("in main pro\n");delay(1000);}re…...

高架学习笔记之需求工程

目录 一、什么是软件需求 二、需求工程 2.1. 需求获取 2.2. 需求分析 2.3. 形成需求规格 2.4. 需求确认 2.5. 需求管理 2.5.1. 变更控制 2.5.2. 版本控制 2.5.3. 需求跟踪 2.5.4. 需求状态跟踪 一、什么是软件需求 软件需求目前没有统一的定义&#xff0c;一般是指用…...

mysql基础2多表查询

多表查询 多表关系: 一对多 案例: 部门 与 员工的关系 关系: 一个部门对应多个员工&#xff0c;一个员工对应一个部门 实现: 在多的一方建立外键&#xff0c;指向一的一方的主键 多对多 案例: 学生 与 课程的关系 关系: 一个学生可以选修多门课程&#xff0c;一门课程也可以…...

Qt 写一个邮件发送程序

最近在完成一个邮箱代替的告警功能&#xff0c;写了一个邮件发送的demo 以下为代码&#xff1a; #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include<QTcpSocket> namespace Ui { class MainWindow; }class MainWindow : public QMainWin…...

swagger3快速使用

目录 &#x1f37f;1.导入依赖 &#x1f32d;2.添加配置文件 &#x1f9c2;3.添加注解 &#x1f96f;4.访问客户端 1.导入依赖 引入swagger3的依赖包 <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artif…...

一键入门Ubuntu22!

目录 一、安装 二、常用目录 三、常用指令 四、用户指令 五、ssh与scp 六、服务相关 七、Python与Pycharm 八、Vim编辑器 九、Ubuntu22下使用Mysql 十、Ubuntu22下使用mongodb 十一、Ubuntu22下使用redis Ubuntu是一个基于Debian的开源操作系统&#xff0c;由Canoni…...

阿里云服务器价格购买价格表,2024新版报价查询

2024年腾讯云服务器优惠价格表&#xff0c;一张表整理阿里云服务器最新报价&#xff0c;阿里云服务器网整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单&#xff0c;大家也可以直接移步到阿里云CLUB中心查看 aliyun.club 当前最新的云服务器优惠券…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...