【C++入门到精通】互斥锁 (Mutex) C++11 [ C++入门 ]

阅读导航
- 引言
- 一、Mutex的简介
- 二、Mutex的种类
- 1. std::mutex (基本互斥锁)
- 2. std::recursive_mutex (递归互斥锁)
- 3. std::timed_mutex (限时等待互斥锁)
- 4. std::recursive_timed_mutex (限时等待递归互斥锁)
- 三、总结
- 温馨提示
引言
在多线程编程中,保证数据的同步和互斥是至关重要的。而互斥锁(Mutex)作为一种常用的同步机制,在C++11标准中被引入,提供了一种简单有效的方式来控制多个线程对共享资源的访问。互斥锁可以确保同一时间只有一个线程可以持有锁,并且其他线程需要等待锁释放后才能继续执行,从而避免了多个线程同时访问共享资源所导致的数据竞争和不一致性问题。本文将详细介绍互斥锁的种类、使用方法以及一些常见的注意事项,帮助读者更好地理解和应用互斥锁来实现线程安全的程序。
一、Mutex的简介
⭕Mutex官方文档

Mutex(互斥量)是一种同步原语,用于实现多线程环境下的资源互斥访问。它允许多个线程同时访问共享资源,但在任何给定时间只能有一个线程能够获得对该资源的独占访问权。Mutex主要用于防止数据竞争和确保数据的一致性。
在C++11之前,开发人员通常使用操作系统提供的互斥机制来实现线程间的同步。而C++11引入的Mutex则提供了一种标准化的、跨平台的解决方案,使得多线程编程更加简单和可靠。
Mutex的基本操作包括锁定(lock)和解锁(unlock)。当一个线程需要访问共享资源时,它会尝试对Mutex进行加锁操作,如果Mutex已经被其他线程锁定,那么该线程将被阻塞,直到Mutex被解锁。一旦线程完成对共享资源的操作,它会释放Mutex,允许其他线程获得对资源的访问权。
使用Mutex可以有效地避免数据竞争和保护共享资源的一致性。然而,Mutex也存在一些潜在问题,如死锁(deadlock)和饥饿(starvation)。为了避免这些问题,开发人员需要仔细设计和管理Mutex的使用,并采用合适的同步机制。
二、Mutex的种类
⭕在C++11中,Mutex总共包了四个互斥量的种类分别是:
| Mutex类型 | 描述 |
|---|---|
| std::mutex | 最基本的互斥锁类型,用于实现线程间的互斥访问。只允许一个线程获得锁,其他线程需要等待锁被释放才能继续执行。 |
| std::recursive_mutex | 与std::mutex类似,但允许同一线程多次获取锁。也就是说,同一线程可以多次对该锁进行加锁操作,每次加锁都需要对应的解锁操作。 |
| std::timed_mutex | 可限时等待的互斥锁类型。与std::mutex类似,但允许线程在尝试获取锁时设置一个超时时间。如果锁在指定的时间内无法被获得,线程将不再等待并返回相应的错误代码。 |
| std::recursive_timed_mutex | 可限时等待的递归互斥锁类型。结合了std::recursive_mutex和std::timed_mutex的特性,允许同一线程多次获取锁,并且可以设置超时时间。 |
以上是四种常见的Mutex类型及其描述,适用于不同的场景,下面我会详细介绍这四个Mutex类型。
1. std::mutex (基本互斥锁)
std::mutex是C++标准库中提供的最基本的互斥锁类型之一。它用于实现线程间的互斥访问,即在一个时间点只允许一个线程获得锁,其他线程需要等待锁被释放才能继续执行。使用std::mutex可以保证多个线程对共享资源的访问顺序,并避免数据竞争产生的问题。
🚨注意:该类的对象之间不能拷贝,也不能进行移动。
⭕std::mutex最常用的三个函数是:
| 函数名 | 描述 |
|---|---|
| lock() | 尝试获取互斥锁。如果未被其他线程占用,则当前线程获取锁;否则阻塞等待锁的释放。 |
| unlock() | 释放互斥锁。如果当前线程持有锁,则释放锁;否则行为未定义。 |
| try_lock() | 尝试获取互斥锁,不会阻塞线程。如果未被其他线程占用,则当前线程获取锁并返回true;否则返回false。 |
这三个函数组成了基本的互斥锁操作,也是使用std::mutex时最常用的三个函数。其中,lock()和unlock()通常需要成对使用,以确保锁得到正确的管理。try_lock()则可以用于一些特殊情况下的非阻塞式加锁操作,例如在轮询等待某个资源时,可以尝试获取锁并立即返回结果。
🚨注意事项
- 线程函数调用lock()时,可能会发生以下三种情况:
- 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
- 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
- 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)
- 线程函数调用try_lock()时,可能会发生以下三种情况:
- 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock释放互斥量。
- 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。
- 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)
2. std::recursive_mutex (递归互斥锁)
std::recursive_mutex是C++标准库中提供的一个递归互斥锁类型,用于实现线程间的互斥访问。与std::mutex相比,std::recursive_mutex可以允许同一线程多次获取互斥锁,而不会导致死锁。简单来说就是允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,释放互斥量时需要调用与该锁层次深度相同次数的 unlock()
std::recursive_mutex定义在<mutex>头文件中。与std::mutex类似,可以通过定义std::recursive_mutex对象来创建一个递归互斥锁。例如:
#include <mutex>
//这里定义了一个名为mtx的std::recursive_mutex对象,用于保护某个共享资源的访问。
std::recursive_mutex mtx;
std::recursive_mutex的主要方法和std::mutex相同,包括lock()、unlock()和try_lock()。这些方法的功能和使用方式也与std::mutex一致。区别在于,当同一线程多次尝试获取std::recursive_mutex时,它不会导致死锁,而是允许同一线程多次获取锁,需要相应次数的解锁操作才能完全释放锁。
3. std::timed_mutex (限时等待互斥锁)
std::timed_mutex是C++标准库中提供的一个可超时等待的互斥锁类型,用于实现线程间的互斥访问。与std::mutex相比,std::timed_mutex在尝试获取锁的时候可以设置超时时间,避免线程由于无法获取锁而一直被阻塞等待,从而提高程序的健壮性。
std::timed_mutex定义在<mutex>头文件中。与std::mutex类似,可以通过定义std::timed_mutex对象来创建一个可超时等待的互斥锁。例如:
#include <mutex>
//这里定义了一个名为mtx的std::timed_mutex对象,用于保护某个共享资源的访问。
std::timed_mutex mtx;
std::timed_mutex的主要方法和std::mutex相同,包括lock()、unlock()和try_lock()。不同的是,std::timed_mutex提供了try_lock_for()和try_lock_until()这两个方法,用于在指定的时间范围内尝试获取互斥锁。
try_lock_for()方法允许线程尝试在指定的时间段内获取互斥锁,如果在指定时间内无法获取锁,则返回false。例如:
std::timed_mutex mtx;
std::chrono::milliseconds timeout(100);if (mtx.try_lock_for(timeout))
{// 成功获取锁// ...mtx.unlock(); // 释放锁
}
else
{// 超时等待,未能获取锁// ...
}
try_lock_until()方法允许线程尝试在指定的时间点之前获取互斥锁,如果在指定时间点之前无法获取锁,则返回false。例如:
std::timed_mutex mtx;
std::chrono::system_clock::time_point deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(100);if (mtx.try_lock_until(deadline))
{// 成功获取锁// ...mtx.unlock(); // 释放锁
}
else
{// 超时等待,未能获取锁// ...
}
4. std::recursive_timed_mutex (限时等待递归互斥锁)
std::recursive_timed_mutex是C++11标准库中提供的一个可递归、可超时等待的互斥锁类型,它是std::timed_mutex的另一个版本。与std::timed_mutex一样,std::recursive_timed_mutex用于实现线程间的互斥访问,但它允许同一线程多次获取锁,从而避免死锁等问题。
std::recursive_timed_mutex定义在<mutex>头文件中。和std::timed_mutex类似,可以通过定义std::recursive_timed_mutex对象来创建一个可递归、可超时等待的互斥锁。例如:
#include <mutex>
//这里定义了一个名为mtx的std::recursive_timed_mutex对象,用于保护某个共享资源的访问。
std::recursive_timed_mutex mtx;
std::recursive_timed_mutex的主要方法和std::timed_mutex相同,包括lock()、unlock()和try_lock()。不同的是,std::recursive_timed_mutex允许同一线程多次获取锁,从而避免死锁等问题。例如:
void foo()
{std::unique_lock<std::recursive_timed_mutex> lock(mtx);// ...bar(); // 调用另一个函数// ...
}void bar()
{std::unique_lock<std::recursive_timed_mutex> lock(mtx); // 可以再次获取锁// ...
}
在上面的例子中,foo()函数和bar()函数都使用了std::unique_lock对象来获取mtx互斥锁。由于std::recursive_timed_mutex允许同一线程多次获取锁,因此bar()函数可以再次获取锁,而不会导致死锁等问题。
与std::timed_mutex类似,std::recursive_timed_mutex也提供了try_lock_for()和try_lock_until()方法,用于在指定的时间范围内尝试获取锁。例如:
std::recursive_timed_mutex mtx;
std::chrono::milliseconds timeout(100);if (mtx.try_lock_for(timeout))
{// 成功获取锁// ...mtx.unlock(); // 释放锁
}
else
{// 超时等待,未能获取锁// ...
}
🚨注意:由于std::recursive_timed_mutex允许同一线程多次获取锁,因此在释放锁之前,必须将锁计数器减少到零。否则,其他线程将无法获取到锁,从而导致死锁等问题。
三、总结
在使用互斥锁时,需要注意正确地加锁和解锁,以避免资源竞争和死锁等问题的发生。在大多数情况下,应优先选择最简单的互斥锁类型std::mutex,只有在需要递归、限时等待功能时才考虑其他类型。选择互斥锁类型应根据具体需求和场景来进行。
通过合理使用互斥锁,可以保证多线程程序的正确性和稳定性,提高多线程程序的性能和并发能力。
温馨提示
感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!
再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!

相关文章:
【C++入门到精通】互斥锁 (Mutex) C++11 [ C++入门 ]
阅读导航 引言一、Mutex的简介二、Mutex的种类1. std::mutex (基本互斥锁)2. std::recursive_mutex (递归互斥锁)3. std::timed_mutex (限时等待互斥锁)4. std::recursive_timed_mutex (限时等待…...
安全狗云原生安全-云甲·云原生容器安全管理系统
随着云计算的快速发展,容器技术逐渐成为主流。然而,随着容器的普及,安全问题也日益突出。为了解决这一问题,安全狗推出了云原生容器安全管理系统——云甲。 云甲是安全狗云原生安全的重要组成部分,它采用了先进的云原生…...
Python 学习路线:介绍、基础语法、数据结构、算法、高级主题、框架及异步编程详解
Python 介绍 Python 是一种 高级 的、解释型 的、通用 的编程语言。其设计哲学强调代码的可读性,使用显著的缩进。Python 是 动态类型 和 垃圾收集 的。 基本语法 设置 Python 环境并开始基础知识。 文章链接:Python 安装与快速入门 变量 变量用于…...
基于Java+SpringBoot+Mybaties-plus+Vue+ElementUI+Vant 电影院订票管理系统 的设计与实现
一.项目介绍 基于SpringBootVue 电影院订票管理系统 分为前端和后端。 前端(用户): 登录后支持查看首页、电影、影院和我的信息 支持查看正在热映和即将上映的电影信息 支持购票(需选择影院座位)、看过(评论…...
轻量级购物小程序H5产品设计经典样例
主要是看到这个产品设计的不错值得借鉴特记录如下: 不过大多数购物app都大致相同,这个算是经典样例,几乎都可以复制,我第一次使用,感觉和顺畅。看上去产品是经过打磨的,布局非常好。内容也很丰富。支持异业…...
final, finally, finalize 的区别?
1.final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 内部类要访问局部变量,局部变量必须定义成 final 类型 2.finally 是异常处理语句结构的一部分,表示总是执行 3.finalize …...
4.使用 Blazor 构建 Web 应用程序
微软官方培训 了解如何通过 Blazor Web 用户界面框架构建你的第一个 Web 应用程序。 https://learn.microsoft.com/zh-cn/training/paths/build-web-apps-with-blazor/?viewaspnetcore-8.0 8个模块 目录 微软官方培训 1.使用 Blazor 进行 Web 开发的简介 2.使用 Blazor…...
CentOS操作学习(二)
上一篇学习了CentOS的常用指令CentOS指令学习-CSDN博客 现在我们接着学习 一、Vi编辑器 这是CentOS中自带的编辑器 三种模式 进入编辑模式后 i:在光标所在字符前开始插入a:在光标所在字符串后开始插入o:在光标所在行的下面另起一新行插入…...
OpenCV技术应用(9)— 视频的暂停播放和继续播放
前言:Hello大家好,我是小哥谈。本节课就手把手教大家如何控制视频的暂停播放和继续播放,希望大家学习之后能够有所收获~!🌈 目录 🚀1.技术介绍 🚀2.实现代码 🚀1.技术介绍…...
C#时间戳转换
时间戳转化为时间 long oldtime1703235741; System.DateTime startTime TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0)); var newtimestartTime.AddMilliseconds(oldtime).ToString("yyyy-MM-dd HH:mm:ss.fff"); 时间转化为时…...
Postgresql源码(118)elog/ereport报错跳转功能分析
1 日志接口 elog.c完成PG中日志的生产、记录工作,对外常用接口如下: 1.1 最常用的ereport和elog ereport(ERROR,(errcode(ERRCODE_UNDEFINED_TABLE),errmsg("relation \"%s\" does not exist",relation->relname)));elog(ERRO…...
Python Selenium中的强大等待设置详解
更多资料获取 📚 个人网站:ipengtao.com 在Web自动化测试中,等待是至关重要的一环,而Selenium提供了丰富的等待设置来确保测试脚本的可靠性和稳定性。本文将深入研究Python Selenium中常用的必备等待设置,包括显式等待…...
ACL实现固定时间访问资源——项目
文章目录 一、前言二、项目拓扑三、项目需求四、配置思路五、配置步骤1 IP地址2 端口类型3 静态路由4 流策略 六、结语 免责声明 本文旨在提供信息和解决问题的建议,观点和建议可能不适用于个人情况,仅供参考!!! 文章中…...
前端学习——关于前端框架的思考
前端框架 我们知道在AngularJS,react,vue等前端框架出现之前,前端开发都是通过js直接操作dom树来实现的,而有了前端框架之后,前端开发基本上不需要在直接操作dom树,相当于在原生html的dom树之间和前端程序…...
大创项目推荐 深度学习+opencv+python实现车道线检测 - 自动驾驶
文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数:3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 &am…...
Linux(二)常用命令
文章目录 一、文件管理命令1.1 chmod1.2 chown1.3 cat1.4 cp1.5 find1.6 head1.7 tail1.8 less1.9 more1.10 mv1.11 rm1.12 touch1.13 vim1.14 >和>>1.15 scp1.16 ln1.17 怎么用命令查看日志 二、文档管理命令2.1 grep2.2 wc2.3 echo 三、磁盘管理命令3.1 cd3.2 df3.3…...
PHP通过mailer发送邮箱
<?php namespace sw\controler\action;require(APP_DIR./extend/PHPMailer/class.phpmailer.php); require(APP_DIR./extend/PHPMailer/class.smtp.php); class action_test_mailer extends Base {public function test(){$smtpemailto"1967899707qq.com";//接收…...
c# OpenCV 基本绘画(直线、椭圆、矩形、圆、多边形、文本)(四)
我们将在这里演示如何使用几何形状和文本注释图像。 Cv2.Line() 绘制直线 Cv2.Ellipse() 绘制椭圆Cv2.Rectangle() 绘制矩形Cv2.Circle() 绘制圆Cv2.FillPoly() 绘制多边形Cv2.PutText() 绘制文本 一、绘制直线 Cv2.Line(image, start_point, end_point, color, thickness) …...
js键盘事件keydown事件,防止重复触发,组合键的配合使用
js键盘事件keydown事件,防止重复触发 键盘事件类型主要有三种: keydown 、keypress(不建议使用,部分浏览器已放弃)和 keyup 。 添加普通键盘keydown事件 // 监听键盘按下事件document.addEventListener(keydown, function(event) {// 输出按…...
【Docker】升级docker或者docker到docker-ce完全保留镜像和容器,不影响原容器使用方法
升级docker或者docker到docker-ce完全保留镜像和容器,不影响原容器使用方法 一、介绍二、升级方法 三、遇到问题说明 以下是我的使用场景,docker升级到docker-ce,但对于docker-ce升级也通用!亲测! 一、介绍 CentOS自带…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
