【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()则可以用于一些特殊情况下的非阻塞式加锁操作,例如在轮询等待某个资源时,可以尝试获取锁并立即返回结果。

🚨注意事项

  1. 线程函数调用lock()时,可能会发生以下三种情况:
    • 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
    • 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
    • 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)
  2. 线程函数调用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++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/263222.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数字图像处理】实验二 图像变换

图像变换 一、实验内容&#xff1a; 1&#xff0e; 熟悉和掌握利用Matlab工具进行数字图像的读、写、显示等数字图像处理基本步骤。 2&#xff0e; 熟练掌握各种图像变换的基本原理及方法。 3&#xff0e; 能够从深刻理解图像变换&#xff0c;并能够思考拓展到一定的应用领域。…

多层负载均衡实现

1、单节点负载均衡 1&#xff09;站点层与浏览器层之间加入了一个反向代理层&#xff0c;利用高性能的nginx来做反向代理 2&#xff09;nginx将http请求分发给后端多个web-server 优点&#xff1a; 1&#xff09;DNS-server不需要动 2&#xff09;负载均衡&#xff1a;通过ngi…

OGG同步异构数据库-表字段变更重新读取异构文件测试验证

OGG同步异构数据库-表字段变更重新读取异构文件测试验证 删除前源和目标端的同步情况&#xff1a; 配置文件信息&#xff1a; 源端&#xff1a; GGSCI (ITSMdoc-236-63) 4> view param etest extract etest setenv (MYSQL_HOME“/data/mysql-5.7.26”) tranlogoptions al…

研究生课程 |《矩阵论》复习

文章目录 【一(18)】填空题【二(10)】范数证明【三(15)】矩阵函数1 计算 e A t e^{At} eAt2 求微分方程的解 【四(10)】QR分解【五(10)】Gerschgorim隔离特征值【六(15)】 A A^ A计算及求解线性方程组1 计算满秩分解2 计算 A A^ A3 判断线性方程组解是否存在 【七(15)】线性…

快速开发教务管理应用,课程表微信小程序源码

介绍 课程表微信小程序源码 快速开发教务管理应用 对接微信公众号每日课表推送 三种导入课表方式可供选择 班级课表导入 爬虫导入课表 学号导入课表

with torch.no_grad()在Pytorch中的应用

with torch.no_grad()在Pytorch中的应用 参考&#xff1a; https://blog.csdn.net/qq_24761287/article/details/129773333 https://blog.csdn.net/sazass/article/details/116668755 在学习Pytorch时&#xff0c;老遇到 with torch.no_grad()&#xff0c;搞不清其作用&#…

P2 H264码流结构分析——Annexb与MP4格式的区别 (中)

前言 从本章开始我们将要学习嵌入式音视频的学习了 &#xff0c;使用的瑞芯微的开发板 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Linux C应用编程&#xff08;概念类&#xff09;_Ch…

如何使用Java的GeoTools地理库计算WGS84坐标下的两个经纬度之间得距离

介绍 本章讲解如何使用Java的GeoTools地理库计算基于WGS84坐标的两点之间的距离。适用于后台服务的距离计算。 GeoTools介绍 GeoTools是开源的Java地理信息计算库。GeoServer地图引擎就是基于GeoTools库构建得地图服务,可以说非常强大。 官网地址:https://docs.geotools.o…

python/C 生成beta分布的随机数

python/C 生成beta分布的随机数 文章目录 python/C 生成beta分布的随机数前言一、beta分布理论知识二、python 生成服从beta分布的随机数三、C语言生成服从beta分布的随机数 前言 想把一个算法用C语言实现&#xff0c;其中涉及到了beta分布取随机数&#xff0c;记录一下结果 一…

【Java】网络编程-TCP回显服务器代码编写

前面我们讲了基于UDP的网络编程 UDP回显服务器 UDP字典服务器 下面我们来讲基于TCP的回显服务编写 1、服务器 import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; impo…

25、新加坡南洋理工、新加坡国立大学提出FBCNet:完美融合FBCSP的CNN,EEG解码SOTA水准![抱歉老师,我太想进步了!]

前言&#xff1a; 阴阳差错&#xff0c;因工作需要&#xff0c;需要查阅有关如何将FBCSP融入CNN中的文献&#xff0c;查阅全网&#xff0c;发现只此一篇文章&#xff0c;心中大喜&#xff0c;心想作者哪家单位&#xff0c;读之&#xff0c;原来是自己大导&#xff08;新加坡工…

冬天天冷早安问候语关心话,愿我的每句话都能带给你温馨

1、送你一声问候&#xff0c;为你驱走冬日严寒&#xff0c;送你一份关怀&#xff0c;为你增添丝丝温暖&#xff0c;送你一句祝福&#xff0c;为你驱走所有不快&#xff0c;送你一份关爱&#xff0c;为你增添幸福无限&#xff0c;天虽寒了&#xff0c;我的关心犹在&#xff0c;愿…

Centos安装Docker及使用

文章目录 配置要求Centos安装Docker卸载docker&#xff08;可选&#xff09;安装docker首先需要大家虚拟机联网&#xff0c;安装yum工具然后更新本地镜像源&#xff1a;然后输入安装docker命令&#xff1a;查看docker的版本 启动docker关闭防火墙接着通过命令启动docker 配置镜…

vscode debug c++代码

需要提前写好CMakeLists.txt 在tasks.json中写好编译的步骤&#xff0c;即tasks&#xff0c;如cmake … 和make -j 在lauch.json中配置可执行文件的路径和需要执行tasks中的哪一个任务 具体步骤&#xff1a; 1.写好c代码和CMakeLists.txt 2.配置tasks.json 终端–>配置任务…

竞赛保研 基于CNN实现谣言检测 - python 深度学习 机器学习

文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于CNN实现谣言检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&am…

Jmeter测试时遇到的各种乱码问题及解决

在Jmeter中显示乱码 遇到的问题&#xff1a;csv文件中有的用户名是中文的&#xff0c;接口使用该用户名入参时&#xff0c;查看结果树中看到&#xff0c;中文名显示为一堆乱码。 解决的方法&#xff1a; 应该是文件编码与jmeter编码不一致导致的。 1&#xff09;新建一个tx…

使用Python从图像中提取表格

有什么作用? 与深度学习解决方案相比,这个轻量级的包不需要训练和最小化参数化。它提供了以下功能: 识别图像和PDF文件中的表格,包括在表格单元级别的边界框。 通过支持OCR服务/工具(Tesseract、PaddleOCR、AWS Textract、Google Vision和Azure OCR目前支持)来提取表格内…

Grafana安装zabbix插件

文章目录 一、在线安装zabbix插件二、离线安装1.下载安装2.grafana配置zabbix数据源 一、在线安装zabbix插件 如果grafana服务器可以联网即可在线安装。 安装方法官网上有&#xff1a;官网链接联网安装比较慢。 二、离线安装 1.下载安装 官网下载链接 [rootnode1 src]# mo…

抖店怎么运营?走通流程是关键!

我是电商珠珠 很多新手在入驻抖店的时候&#xff0c;都不知道前期怎么去运营。 运营的技巧很多&#xff0c;牵扯到很多细节&#xff0c;跑通流程是关键。 所以&#xff0c;今天就来给大家讲讲抖店的几个基本流程&#xff0c;只有将流程跑通了才能去更快的抠细节上的东西。 …

算法题系列6·删除有序数组中的重复项

目录 题目描述 思路分析 实现 题目描述 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 …