C++——互斥量

article/2025/9/17 16:55:07

文章目录

  • 一、基本知识
  • 二、独占互斥量mutex
    • 1.mutex的介绍
    • 2.mutex的成员函数
    • 3.实例演示
  • 三、lock_guard和unique_lock的使用和区别
  • 四、递归互斥量recursive_mutex
    • 1.基本知识
    • 2.演示示例
  • 五、带超时的互斥量std::timed_mutex和std::recursive_timed_mutex


一、基本知识

C++11提供如下4种语义的互斥量(mutex)
std::mutex,独占的互斥量,不能递归使用。
std::time_mutex,带超时的独占互斥量,不能递归使用。
std::recursive_mutex,递归互斥量,不带超时功能。
std::recursive_timed_mutex,带超时的递归互斥量。
使用时需要包含头文件

#include<mutex>

二、独占互斥量mutex

1.mutex的介绍

下面以 std::mutex 为例介绍 C++11 中的互斥量用法。
std::mutex 是C++11 中最基本的互斥量,std::mutex 对象提供了独占所有权的特性——即不支持递归地对 std::mutex 对象上锁,而 std::recursive_lock 则可以递归地对互斥量对象上锁。

2.mutex的成员函数

构造函数:std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于unlocked 状态的。
lock():调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:
①如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
② 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
③如果当前互斥量被当前调用线程锁住,则会产生死锁。
unlock(): 解锁,释放对互斥量的所有权。
try_lock():尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况:这些
①如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。
②如果当前互斥量被其他线程锁住,则当前调用线程返回
false,而并不会被阻塞掉。
③如果当前互斥量被当前调用线程锁住,则会产生死锁。

3.实例演示

#include<iostream>
#include<mutex>
#include<thread>
using namespace std;std::mutex mtx;
int counter;void pthread_fun()
{for (int i = 0; i < 10000; i++){mtx.lock();counter++;mtx.unlock();}
}int main() 
{thread pthread[10];for (int i = 0; i < 10; i++){pthread[i] = thread(pthread_fun);}for (int i = 0; i < 10; i++){pthread[i].join();}cout << counter << endl;return 0;
}

运行结果:
在这里插入图片描述

三、lock_guard和unique_lock的使用和区别

相对于手动lock和unlock,我们可以使用RAII(通过类的构造析构)来实现更好的编码方式。
这里涉及到unique_lock,lock_guard的使用。
C++相较于C引入了很多新的特性, 比如可以在代码中抛出异常, 如果还是按照以前的加锁解锁的话代码会极为复杂繁琐。

unique_lock,lock_guard的区别:
unique_lock与lock_guard都能实现自动加锁和解锁,但是前者更加灵活,能实现更多的功能。
unique_lock可以进行临时解锁和再上锁,如在构造对象之后使用lck.unlock()就可以进行解锁,lck.lock()进行上锁,而不必等到析构时自动解锁。

#include<iostream>
#include<mutex>
#include<thread>
using namespace std;std::mutex mtx;
int counter;void pthread_fun()
{for (int i = 0; i < 10000; i++){//mtx.lock();error,必须和unlock配对,否则锁得不到释放std::unique_lock<mutex>lck(mtx);//right 这句语句执行会自动上锁,生存期到了之后会调动析构函数自动释放锁counter++;//lck.unlock();//可以手动释放,也可以等过了作用域,lck调用析构函数析构自动释放锁}
}int main() 
{thread pthread[10];for (int i = 0; i < 10; i++){pthread[i] = thread(pthread_fun);}for (int i = 0; i < 10; i++){pthread[i].join();}cout << counter << endl;return 0;
}

对于locak_guard来说,unique_lock显得比较灵活一点,因为在lock_guard和unique_lock都可以借用对象有生存期调用析构函数释放锁的情况下,unique_lock可以还自己上锁释放锁,显得比较灵活一点,所以平时比较经常使用unique_lock。

四、递归互斥量recursive_mutex

1.基本知识

递归锁允许同一个线程多次获取该互斥锁,可以用来解决同一线程需要多次获取互斥量时死锁的问题。
虽然递归锁能解决这种情况的死锁问题,但是尽量不要使用递归锁,主要原因如下:
(1)需要用到递归锁的多线程互斥处理本身就是可以简化的,允许递归很容易放纵复杂逻辑的产生,并且产生晦涩,当要使用递归锁的时候应该重新审视自己的代码是否一定要使用递归锁;
(2)递归锁比起非递归锁,效率会低;
(3)递归锁虽然允许同一个线程多次获得同一个互斥量,但可重复获得的最大次数并未具体说明,一旦超过一定的次数,再对lock进行调用就会抛出std::system错误。

2.演示示例

#include<iostream>
#include<mutex>
#include<thread>
using namespace std;std::recursive_mutex rc_mtx;//可递归的锁
std::mutex mtx;
int g_val = 0;void fun(int&& x)
{if (x == 0) return;std::unique_lock<std::recursive_mutex>lck(rc_mtx);//right//下面语句error 会造成死锁,因为递归的时候锁没有释放,再次获得锁,造成死锁。//std::unique_lock<std::mutex>lck(mtx);g_val++;x--;fun(std::move(x));
}int main()
{std::thread pthreads[5];for (int i = 0; i < 5; i++){pthreads[i] = std::thread(fun, 5);}for (int i = 0; i < 5; i++){pthreads[i].join();}cout << g_val << endl;return 0;
}

运行结果:
在这里插入图片描述

五、带超时的互斥量std::timed_mutex和std::recursive_timed_mutex

std::timed_mutex比std::mutex多了两个超时获取锁的接口:
try_lock_for:获取锁的时间,过了这个时间会释放锁
try_lock_until:尝试获得锁,直到过了某个时间,返回false。

#include<iostream>
#include<mutex>
#include<thread>
using namespace std;std::mutex mtx;
std::timed_mutex t_mtx;
int g_val = 0;//如果fun写成下面那样会发生死锁,因为第一个获取锁的线程没有释放锁,其他线程都会阻塞住
/*
void fun()
{mtx.lock();g_val++;
}
*/std::chrono::milliseconds timeout(1000);void fun()
{t_mtx.try_lock_for(timeout);//获取锁1000毫秒,1000毫秒后会释放锁g_val++;
}int main()
{std::thread pthreads[5];for (int i = 0; i < 5; i++){pthreads[i] = std::thread(fun);}for (int i = 0; i < 5; i++){pthreads[i].join();}cout << g_val << endl;return 0;
}

http://chatgpt.dhexx.cn/article/RYCnTnNn.shtml

相关文章

Linux——什么是互斥与互斥锁

目录 一.前提&#xff1a;临界区 & 临界资源 二.什么是互斥 &#xff08;一&#xff09;.互斥概念 &#xff08;二&#xff09;.为什么需要互斥 三.互斥锁介绍 &#xff08;一&#xff09;.互斥锁的概念 &#xff08;二&#xff09;.互斥锁的使用 ①系统API接口 ②…

线程的互斥与同步

线程的互斥与同步 线程的互斥简单的抢票程序互斥量初始化互斥量销毁互斥量互斥量加锁和解锁 互斥量实现原理 可重入VS线程安全概念常见的线程不安全的情况常见的线程安全的情况常见不可重入的情况常见可重入的情况可重入与线程安全联系可重入与线程安全区别 死锁死锁四个必要条…

FreeRTOS个人笔记-互斥量

根据个人的学习方向&#xff0c;学习FreeRTOS。由于野火小哥把FreeRTOS讲得比较含蓄&#xff0c;打算在本专栏尽量细化一点。作为个人笔记&#xff0c;仅供参考或查阅。 配套资料&#xff1a;FreeRTOS内核实现与应用开发实战指南、野火FreeRTOS配套视频源码、b站野火FreeRTOS视…

C++ 互斥锁原理以及实际使用介绍

兄弟姐妹们&#xff0c;我又回来了&#xff0c;今天带来实际开发中都需要使用的互斥锁的内容&#xff0c;主要聊一聊如何使用互斥锁以及都有哪几种方式实现互斥锁。实现互斥&#xff0c;可以有以下几种方式&#xff1a;互斥量&#xff08;Mutex&#xff09;、递归互斥量&#x…

MySQL mutex互斥锁

在事务机制中&#xff0c;锁机制是为了保证高并发&#xff0c;数据一致性的重要实现方式。MySQL除了Innodb引擎层面的行锁&#xff0c;还有latch锁。latch锁的作用资源协调处理。包含表句柄&#xff0c;线程&#xff0c;cpu线程&#xff0c;内存池等。其保证非常短时间内快速处…

uCOSii中的互斥信号量

uCOSii中的互斥信号量 一、互斥型信号量项管理 (MUTUAL EXCLUSION SEMAPHORE MANAGEMENT) OSMutexAccept() 无条件等待地获取互斥型信号量 OSMutexCreate() 建立并初始化一个互斥型信号量 OSMutexDel() 删除互斥型信号量 OSMutexPend() 等待一个互斥型信号量 OSMutexPost…

互斥信号量

目录 1、Creat 2、Delete 3、Wait 4、Post 5、Statu 互斥信号量 在介绍二进制信号量时&#xff0c;曾讨论到如果二进制信号量创建时设置参数 bInitValue 为TRUE&#xff0c;则可以用于互斥访问共享资源。实际上&#xff0c;SylixOS 的二进制信号量实现的互斥性是将一个变量…

UCOS-III 互斥量

互斥量 一、互斥量基本概念二、互斥量优先级继承机制三、互斥量应用场景四、互斥量运作机制五、互斥量创建流程1、定义互斥量2、创建互斥量 六、互斥量接口函数1、创建互斥量函数OSMutexCreate()2、删除互斥量函数 OSMutexDel()3、获取互斥量函数 OSMutexPend()4、释放互斥量函…

互斥量知识

文章目录 互斥量1、基本概念2、互斥量的优先级继承机制3、互斥量应用场景4、互斥量运行机制5、互斥量控制块6、互斥量函数接口&#xff08;1&#xff09;互斥量创建函数 xSemaphoreCreateMutex()&#xff08;2&#xff09;递归互斥量创建函数 xSemaphoreCreateRecursiveMutex()…

同步和互斥

同步和互斥 竞争与协作 在单核 CPU 系统里&#xff0c;为了实现多个程序同时运行的假象&#xff0c;操作系统通常以时间片调度的方式&#xff0c;让每个进程执行每次执行一个时间片&#xff0c;时间片用完了&#xff0c;就切换下一个进程运行&#xff0c;由于这个时间片的时间很…

多线程的同步与互斥(互斥锁、条件变量、读写锁、自旋锁、信号量)

文章目录 一、同步与互斥的概念二、互斥锁&#xff08;同步&#xff09;三、条件变量&#xff08;同步&#xff09;1、线程的条件变量实例12、线程的条件变量实例23、虚假唤醒(spurious wakeup) 四、读写锁&#xff08;同步&#xff09;五、自旋锁&#xff08;同步&#xff09;…

同步和互斥区别

互斥的概念 由于多线程执行操作共享变量的这段代码可能会导致竞争状态&#xff0c;因此我们将此段代码称为临界区&#xff08;critical section&#xff09;&#xff0c;它是访问共享资源的代码片段&#xff0c;一定不能给多线程同时执行。 我们希望这段代码是互斥&#xff0…

操作系统——互斥的定义及实现

一、进程互斥的定义 所谓进程互斥,指的是对某个系统资源,一个进程正在使用它,另外一个想用它的进程就必须等待,而不能同时使用 。进程互斥是多道程序系统中进程间存在的一种源于资源共享的制约关系,也称间接制约关系,主要是由被共享资源的使用性质所决定的。 二、互斥…

Fisher判别分析详解

Fisher判别分析 将高维度空间的样本投影到低维空间上&#xff0c;使得投影后的样本数据在新的子空间上有最小的类内距离以及最大的类间距离&#xff0c;使得在该子空间上有最佳的可分离性 可以看出右侧投影后具有更好的可分离性。 Fisher判别分析和PCA差别 刚学完感觉两个很…

基于spss的多元统计分析 之 聚类分析+判别分析(2/8)

实验目的&#xff1a; 1&#xff0e;掌握聚类分析及判别分析的基本原理&#xff1b; 2&#xff0e;熟悉掌握SPSS软件进行聚类分析及判别分析的基本操作&#xff1b; 3&#xff0e;利用实验指导的实例数据&#xff0c;上机熟悉聚类分析及判别分析方法。 实验前预习&#xff1a;…

机器学习——线性判别分析

目录 线性判别分析 LDA的降维过程 案例&#xff1a;鸢尾花(Iris) 代码演示 数据集 局部线性嵌入 线性判别分析 线性判别分析&#xff08;LDA&#xff09;是一种有监督的线性降维算法。与PCA不同&#xff0c;LDA是为了使降维后的数据点尽可能地容易被区分。 线性判别分析…

MATLAB判别分析例题,判别分析的matlab实现案例.doc

判别分析的matlab实现案例.doc 读取EXAMP10_01XLS中数据&#xff0c;进行距离判别读取数据读取文件EXAMP10_01XLS的第1个工作表中C2F51范围的数据&#xff0c;即全部样本数据&#xff0c;包括未判企业SAMPLEXLSREAD EXAMP10_01XLS , , C2F51 读取文件EXAMP10_01XLS的第1个工作…

SAS数据分析之判别分析

判别分析与聚类分析有非常类似的特性&#xff0c;因此&#xff0c;在多数数据分析的教材中&#xff0c;这两章是一前一后出现的&#xff0c;简而言之&#xff0c;聚类分析&#xff0c;其实是判别分析的基础&#xff0c;即在聚类分析的基础上&#xff0c;总结出各类的权值&#…

线性判别分析

线性判别分析&#xff08;LDA&#xff09;是一种经典的线性学习方法。其思想是&#xff1a;给定训练样例集&#xff0c;设法将样例投影到一条直线上&#xff0c;使得同类样例的投影点尽可能接近、异类样例的投影点尽可能远离。如图所示的二分类示意图&#xff1a; 损失函数的…

sas判别分析

#判别分析两大类&#xff1a;Fisher&Bayes #neighbbor&#xff1a;马氏距离和欧氏距离&#xff1b; #典型判别分析&#xff0c;联系典型相关分析#组变量相关&#xff0c;最后得到的每组内变量的线性组合作为典型变量 #协方差矩阵&#xff0c;举个栗子,设一组特征x(a1,a…