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

C++中的 互斥量

1.概念:

为什么:线程的异步性,不是按照时间来的!!!

C++并发以及多线程的秘密-CSDN博客

目的

多线程编程中,当多个线程可能同时访问和修改共享资源时,会导致数据不一致或程序错误。互斥量提供了一种加锁机制,线程在访问共享资源前必须先获取互斥量的锁,访问结束后释放锁,这样其他线程才能获取锁并访问共享资源。

原理:

互斥量内部维护一个状态,表示它是否被锁定。当一个线程成功获取锁时,互斥量被锁定,其他线程试图获取锁时会被阻塞,直到锁被释放

2.基本用法

C++ 标准库在 <mutex> 头文件中提供了几种类型的互斥量, std::mutex std::timed_mutex

lock函数和unlock函数

#include<mutex>
#include<thread>
#include<iostream>
using namespace std;int sharedResource = 0;
std::mutex mtx;
void increment() {for (int i = 0; i < 10; ++i) {mtx.lock();++sharedResource;cout<<++sharedResource<<endlmtx.unlock();}
}void decrement() {for (int i = 0; i < 10; ++i) {mtx.lock();--sharedResource;cout<<sharedResource<<endl;mtx.unlock();}
}
int main(){std::thread thread1(increment);std::thread thread2(decrement);thread1.join();thread2.join();std::cout << "Final value of sharedResource: " << sharedResource << std::endl;return 0;}

结果如下:

1
2
3
4
5
6
7
8
9
10
9
8
7
6
5
4
3
2
1
0

结果展现清晰知道会有独占思想

2.lock_guard函数

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;std::mutex mtx;
int n=0;void func1() {lock_guard<mutex> lock(mtx);n++;
}
void func2() {lock_guard<mutex> lock(mtx);n--;
}int main() {thread t1(func1);cout << n << endl;thread t2(func2);cout << n << endl;t1.join();t2.join();
}

3.死锁以及解决办法

概念:

死锁是指在多线程或多进程环境中,两个或多个执行单元(线程、进程等)因竞争系统资源或彼此通信而造成的一种阻塞现象,若无外力作用,这些执行单元都将无法推进。

死锁产生的必要条件

  1. 互斥条件:资源在同一时间只能被一个执行单元使用。例如,打印机在打印一份文档时,不能同时为另一份文档服务。

  2. 占有并等待条件:一个执行单元持有至少一个资源,并在等待获取其他执行单元持有的额外资源。比如,线程 A 持有资源 R1,同时等待资源 R2,而资源 R2 被线程 B 持有。

  3. 不可剥夺条件:资源不能被强制从占有者手中夺走,只能由占有者主动释放。例如,一个线程获得了一个文件的独占访问权,其他线程不能强行剥夺这个权限。

  4. 循环等待条件:存在一个执行单元的循环链,链中的每个执行单元都在等待下一个执行单元持有的资源。例如,线程 A 等待线程 B 持有的资源,线程 B 等待线程 C 持有的资源,而线程 C 又等待线程 A 持有的资源。

#include <iostream>
#include <mutex>
#include <thread>std::mutex mutex1;
std::mutex mutex2;void threadFunction1() {mutex1.lock();std::cout << "Thread 1 has locked mutex1" << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));mutex2.lock();std::cout << "Thread 1 has locked mutex2" << std::endl;mutex2.unlock();mutex1.unlock();
}void threadFunction2() {mutex2.lock();std::cout << "Thread 2 has locked mutex2" << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));mutex1.lock();std::cout << "Thread 2 has locked mutex1" << std::endl;mutex1.unlock();mutex2.unlock();
}int main() {std::thread thread1(threadFunction1);std::thread thread2(threadFunction2);thread1.join();thread2.join();return 0;
}
结果//Thread 1 has locked mutex1Thread 2 has locked mutex2

解析:

在上述代码中,threadFunction1 先锁定 mutex1,然后等待 mutex2,而 threadFunction2 先锁定 mutex2,然后等待 mutex1。如果两个线程同时运行,就会满足死锁的四个条件,导致死锁发生。线程 1 持有 mutex1 并等待 mutex2,线程 2 持有 mutex2 并等待 mutex1,形成循环等待,且资源不能被剥夺,从而造成死锁。

如何解决?

利用另外一个lock_guard

std::lock_guard 是基于 RAII(资源获取即初始化)机制的互斥量管理类。它在构造时自动锁定互斥量,在析构时自动解锁,从而简化了代码并避免手动解锁失败的风险

代表adopt_lock 以及手动锁了(就是用lock)

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;std::mutex mutex1;
std::mutex mutex2;
;void threadFunction1() {lock(mutex1,mutex2);lock_guard<mutex>lock1(mutex1,adopt_lock);lock_guard<mutex>lock2(mutex2,adopt_lock);std::cout << "Thread 1 has locked mutex1 and mutex2" << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Thread 1 is working with both mutexes" << std::endl;
}void threadFunction2() {lock(mutex1,mutex2);lock_guard<mutex>lock1(mutex2,adopt_lock);lock_guard<mutex>lock2(mutex1,adopt_lock);std::cout << "Thread 1 has locked mutex1 and mutex2" << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Thread 1 is working with both mutexes" << std::endl;}int main() {std::thread thread1(threadFunction1);std::thread thread2(threadFunction2);thread1.join();thread2.join();return 0;
}
Thread 1 has locked mutex1 and mutex2
Thread 1 is working with both mutexes
Thread 1 has locked mutex1 and mutex2
Thread 1 is working with both mutexes

4.unique_lock

std::lock_guard是一个简单的 RAII(Resource Acquisition Is Initialization)类,用于在其生命周期内自动锁定和解锁互斥量。它的优点是简单易用,开销小。然而,std::unique_lock提供了更多的功能和灵活性,在很多场景下可以取代std::lock_guard:

  • 延迟锁定std::unique_lock可以在构造时不立即锁定互斥量,而std::lock_guard在构造时总是立即锁定互斥量。

  • 锁定所有权的转移std::unique_lock允许通过移动语义转移锁定的所有权,这在函数间传递锁定状态时非常有用。

  • 成员函数操作std::unique_lock提供了如lock()try_lock()try_lock_for()try_lock_until()unlock()等成员函数,允许对锁定状态进行更细粒度的控制,而std::lock_guard只在构造和析构时自动锁定和解锁,没有提供这些操作。

4.1三种构造方法

1.std::adopt_lock_t:与std::lock_guard类似,当使用std::adopt_lock作为第二个参数时,表明调用者已经手动锁定了互斥量,std::unique_lock在构造时不会再次锁定,而是在析构时负责解锁。例如:

std::mutex mtx;
mtx.lock();
std::unique_lock<std::mutex> lock(mtx, std::adopt_lock);

2.std::defer_lock_t:使用std::defer_lock作为第二个参数时,std::unique_lock构造时不会锁定互斥量,而是处于未锁定状态。之后可以通过调用lock()try_lock()等成员函数来锁定互斥量。例如

std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
if (lock.try_lock()) {// 成功锁定,执行临界区代码lock.unlock();
}

3.std::try_to_lock_t:使用std::try_to_lock作为第二个参数时,std::unique_lock构造时会尝试锁定互斥量,但不会阻塞等待。如果成功锁定,unique_lock对象拥有锁定;否则,处于未锁定状态。例如

std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);
if (lock.owns_lock()) {// 成功锁定,执行临界区代码lock.unlock();
}

4.2成员函数

  • lock():锁定关联的互斥量。如果互斥量已被锁定,调用线程将阻塞直到互斥量可用。

  • try_lock():尝试锁定关联的互斥量,不会阻塞。如果成功锁定,返回true;否则,返回false

  • try_lock_for(duration):尝试在指定的时间段内锁定关联的互斥量。如果在指定时间内成功锁定,返回(一般参数为chrono::seconds())同理

  • try_lock_until(time_point) 只能time_mutex用

  • unlock():解锁关联的互斥量。

  • owns_lock():返回unique_lock对象是否拥有互斥量的锁定。

  • release():释放unique_lock对象对互斥量的所有权,返回指向关联互斥量的指针,并且unique_lock对象变为不拥有任何互斥量

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>// 使用 std::timed_mutex 来支持带超时的锁定操作
std::timed_mutex mtx;// 线程函数
void worker(int id) {// 使用 unique_lock 管理互斥量std::unique_lock<std::timed_mutex> lock(mtx, std::defer_lock);// 尝试锁定互斥量,不会阻塞if (lock.try_lock()) {std::cout << "Thread " << id << " acquired the lock using try_lock()." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));lock.unlock();} else {std::cout << "Thread " << id << " failed to acquire the lock using try_lock()." << std::endl;}// 尝试在指定时间段内锁定互斥量if (lock.try_lock_for(std::chrono::seconds(2))) {std::cout << "Thread " << id << " acquired the lock using try_lock_for()." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));lock.unlock();} else {std::cout << "Thread " << id << " failed to acquire the lock using try_lock_for()." << std::endl;}// 尝试在指定时间点前锁定互斥量auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(2);if (lock.try_lock_until(timeout)) {std::cout << "Thread " << id << " acquired the lock using try_lock_until()." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));lock.unlock();} else {std::cout << "Thread " << id << " failed to acquire the lock using try_lock_until()." << std::endl;}// 正常锁定互斥量lock.lock();std::cout << "Thread " << id << " acquired the lock using lock()." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));// 检查是否拥有互斥量的锁定if (lock.owns_lock()) {std::cout << "Thread " << id << " owns the lock." << std::endl;}// 释放 unique_lock 对互斥量的所有权std::timed_mutex* released_mtx = lock.release();if (!lock.owns_lock()) {std::cout << "Thread " << id << " released the lock using release()." << std::endl;}// 手动解锁互斥量released_mtx->unlock();
}int main() {// 创建两个线程std::thread t1(worker, 1);std::thread t2(worker, 2);// 等待线程完成t1.join();t2.join();return 0;
}

相关文章:

C++中的 互斥量

1.概念&#xff1a; 为什么&#xff1a;线程的异步性&#xff0c;不是按照时间来的&#xff01;&#xff01;&#xff01; C并发以及多线程的秘密-CSDN博客 目的 多线程编程中&#xff0c;当多个线程可能同时访问和修改共享资源时&#xff0c;会导致数据不一致或程序错误。…...

直接法估计相机位姿

引入 在前面的文章&#xff1a;运动跟踪——Lucas-Kanade光流中&#xff0c;我们了解到特征点法存在一些缺陷&#xff0c;并且用光流法追踪像素点的运动来替代特征点法进行特征点匹配的过程来解决这些缺陷。而这篇文章要介绍的直接法则是通过计算特征点在下一时刻图像中的位置…...

PHP动态网站建设

如何配置虚拟主机 1. 学习提纲 本地发布与互联网发布&#xff1a;介绍了如何通过本地IP地址和互联网域名发布网站。 虚拟主机配置与访问&#xff1a;讲解了如何配置虚拟主机&#xff0c;并通过自定义域名访问不同的站点目录。 Web服务器配置&#xff1a;详细说明了如何配置A…...

【gRPC】Java高性能远程调用之gRPC详解

gRPC详解 一、什么是gRPC&#xff1f;二、用proto生成代码2.1、前期准备2.2、protobuf插件安装 三、简单 RPC3.1、开发gRPC服务端3.2、开发gRPC客户端3.3、验证gRPC服务 四、服务器端流式 RPC4.1、开发一个gRPC服务&#xff0c;类型是服务端流4.2、开发一个客户端&#xff0c;调…...

数据结构知识学习小结

一、动态内存分配基本步骤 1、内存分配简单示例&#xff1a; 个人对于示例的理解&#xff1a; 定义一个整型的指针变量p&#xff08;着重认为它是一个“变量”我觉得可能会更好理解&#xff09;&#xff0c;这个变量用来存地址的&#xff0c;而不是“值”&#xff0c;malloc函…...

分布式锁—2.Redisson的可重入锁一

大纲 1.Redisson可重入锁RedissonLock概述 2.可重入锁源码之创建RedissonClient实例 3.可重入锁源码之lua脚本加锁逻辑 4.可重入锁源码之WatchDog维持加锁逻辑 5.可重入锁源码之可重入加锁逻辑 6.可重入锁源码之锁的互斥阻塞逻辑 7.可重入锁源码之释放锁逻辑 8.可重入锁…...

计算机毕业设计SpringBoot+Vue.js球队训练信息管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

FFmpeg入门:最简单的音视频播放器

FFmpeg入门&#xff1a;最简单的音视频播放器 前两章&#xff0c;我们已经了解了分别如何构建一个简单和音频播放器和视频播放器。 FFmpeg入门&#xff1a;最简单的音频播放器 FFmpeg入门&#xff1a;最简单的视频播放器 本章我们将结合上述两章的知识&#xff0c;看看如何融…...

java 查找两个集合的交集部分数据

利用了Java 8的Stream API&#xff0c;代码简洁且效率高 import java.util.stream.Collectors; import java.util.List; import java.util.HashSet; import java.util.Set;public class ListIntersection {public static List<Long> findIntersection(List<Long> …...

【系统架构设计师】以数据为中心的体系结构风格

目录 1. 说明2. 仓库体系结构风格3. 黑板体系结构风格 1. 说明 1.以数据为中心的体系结构风格主要包括仓库体系结构风格和黑板体系结构风格。 2. 仓库体系结构风格 1.仓库&#xff08;Repository&#xff09;是存储和维护数据的中心场所。2.在仓库风格中&#xff0c;有两种不…...

通过HTML有序列表(ol/li)实现自动递增编号的完整解决方案

以下是通过HTML有序列表(ol/li)实现自动递增编号的完整解决方案&#xff1a; <!DOCTYPE html> <html> <head> <style> /* 基础样式 */ ol {margin: 1em 0;padding-left: 2em; }/* 方案1&#xff1a;默认数字编号 */ ol.default {list-style-type: dec…...

【Python 数据结构 4.单向链表】

目录 一、单向链表的基本概念 1.单向链表的概念 2.单向链表的元素插入 元素插入的步骤 3.单向链表的元素删除 元素删除的步骤 4.单向链表的元素查找 元素查找的步骤 5.单向链表的元素索引 元素索引的步骤 6.单向链表的元素修改 元素修改的步骤 二、Python中的单向链表 ​编辑 三…...

基于 vLLM 部署 LSTM 时序预测模型的“下饭”(智能告警预测与根因分析部署)指南

Alright,各位看官老爷们,准备好迎接史上最爆笑、最通俗易懂的 “基于 vLLM 部署 LSTM 时序预测模型的智能告警预测与根因分析部署指南” 吗? 保证让你笑出猪叫,看完直接变身技术大咖!🚀😂 咱们今天的主题,就像是要打造一个“智能运维小管家”! 这个小管家,不仅能提…...

Java多线程与高并发专题——ConcurrentHashMap 在 Java7 和 8 有何不同?

引入 上一篇我们提到HashMap 是线程不安全的&#xff0c;并推荐使用线程安全同时性能比较好的 ConcurrentHashMap。 而在 Java 8 中&#xff0c;对于 ConcurrentHashMap 这个常用的工具类进行了很大的升级&#xff0c;对比之前 Java 7 版本在诸多方面都进行了调整和变化。不过…...

NL2SQL-基于Dify+阿里通义千问大模型,实现自然语音自动生产SQL语句

本文基于Dify阿里通义千问大模型&#xff0c;实现自然语音自动生产SQL语句功能&#xff0c;话不多说直接上效果图 我们可以试着问他几个问题 查询每个部门的员工数量SELECT d.dept_name, COUNT(e.emp_no) AS employee_count FROM employees e JOIN dept_emp de ON e.emp_no d…...

LeetCode 1328.破坏回文串:贪心

【LetMeFly】1328.破坏回文串&#xff1a;贪心 力扣题目链接&#xff1a;https://leetcode.cn/problems/break-a-palindrome/ 给你一个由小写英文字母组成的回文字符串 palindrome &#xff0c;请你将其中 一个 字符用任意小写英文字母替换&#xff0c;使得结果字符串的 字典…...

计算机视觉|ViT详解:打破视觉与语言界限

一、ViT 的诞生背景 在计算机视觉领域的发展中&#xff0c;卷积神经网络&#xff08;CNN&#xff09;一直占据重要地位。自 2012 年 AlexNet 在 ImageNet 大赛中取得优异成绩后&#xff0c;CNN 在图像分类任务中显示出强大能力。随后&#xff0c;VGG、ResNet 等深度网络架构不…...

//定义一个方法,把int数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,并在控制台输出结果

import java.util.Scanner; public class cha{ public static void main(String[] args){//定义一个方法&#xff0c;把int数组中的数据按照指定的格式拼接成一个字符串返回&#xff0c;调用该方法&#xff0c;并在控制台输出结果//eg&#xff1a; 数组为&#xff1a;int[] arr…...

Python快捷手册

Python快捷手册 后续会陆续更新Python对应的依赖或者工具使用方法 文章目录 Python快捷手册[toc]1-依赖1-词云小工具2-图片添加文字3-BeautifulSoup网络爬虫4-Tkinter界面绘制5-PDF转Word 2-开发1-多线程和队列 3-运维1-Requirement依赖2-波尔实验室3-Anaconda3使用教程4-CentO…...

QT5 GPU使用

一、问题1 1、现象 2、原因分析 出现上图错误&#xff0c;无法创建EGL表面&#xff0c;错误&#xff1d;0x300b。申请不上native window有可能是缺少libqeglfs-mali-integration.so 这个库 3、解决方法 需要将其adb push 到小机端的/usr/lib/qt5/plugins/egldeviceintegrat…...

别再让电池充不满!用CN3791芯片设计太阳能充电电路,这几个调试坑我帮你踩了

太阳能充电电路实战&#xff1a;CN3791芯片调试避坑指南 当阳光洒在太阳能板上&#xff0c;理论上我们应该获得源源不断的清洁能源。但现实往往比理想骨感得多——尤其当你发现精心设计的CN3791充电电路始终无法将锂电池充满时。这不是芯片的错&#xff0c;而是我们在参数设置和…...

终极无边框游戏窗口指南:三步实现无缝多任务体验

终极无边框游戏窗口指南&#xff1a;三步实现无缝多任务体验 【免费下载链接】Borderless-Gaming Play your favorite games in a borderless window; no more time consuming alt-tabs. 项目地址: https://gitcode.com/gh_mirrors/bo/Borderless-Gaming 你是否厌倦了在…...

OpenClaw企业微信渠道配置教程|API模式+长连接+全部授权

OpenClaw 连接企业微信完整图文教程 前置准备 下载小龙虾open claw一键装机包&#xff08;www.totom.top&#xff09;并安装 已安装并可以正常打开 OpenClaw Windows。 OpenClaw 顶部 Gateway 状态保持在线。 已安装并登录企业微信客户端。 当前企业微信账号具备创建和管理…...

Harness层加密传输:Agent通信安全

Harness层加密传输&#xff1a;Agent通信安全 标题选项 《CI/CD管道的“隐形长城”&#xff1a;深入Harness Agent通信全链路加密传输机制》《从握手到数据&#xff1a;拆解Harness云原生平台Agent-Manager层加密传输的核心原理与实践》《DevOps安全必知&#xff1a;Harness如…...

WRF-CHEM模拟翻车?可能是你的namelist.chem没设对(附MEIC数据实战配置清单)

WRF-CHEM模拟异常排查指南&#xff1a;MEIC数据与namelist.chem的深度适配 当WRF-CHEM模拟结果出现异常时&#xff0c;很多用户会第一时间怀疑MEIC数据处理环节的问题&#xff0c;但实际上&#xff0c;namelist.chem参数与MEIC特性的匹配度才是更隐蔽的关键因素。本文将带您深入…...

3分钟搞定MASA全家桶汉化包:让Minecraft模组界面说中文的完整指南

3分钟搞定MASA全家桶汉化包&#xff1a;让Minecraft模组界面说中文的完整指南 【免费下载链接】masa-mods-chinese 一个masa mods的汉化资源包 项目地址: https://gitcode.com/gh_mirrors/ma/masa-mods-chinese 你是否因为MASA模组的英文界面而感到困扰&#xff1f;想要…...

5种高效集成方案:Bilibili视频解析API的终极实用指南

5种高效集成方案&#xff1a;Bilibili视频解析API的终极实用指南 【免费下载链接】bilibili-parse bilibili Video API 项目地址: https://gitcode.com/gh_mirrors/bi/bilibili-parse bilibili-parse是一款基于PHP实现的B站视频解析API工具&#xff0c;通过简洁优雅的技…...

HoRain云--VS Code 创建与使用 Skill

&#x1f3ac; HoRain 云小助手&#xff1a;个人主页 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个超棒的服务器购买网站&#xff0c;性价比超高&#xff0c;大内存超划算&#xff01;忍不住分享一下给大家。点击跳转到网站。 目录 ⛳️ 推荐 …...

【NotebookLM统计方法选择权威指南】:20年数据科学家亲授5大避坑法则与3步决策框架

更多请点击&#xff1a; https://kaifayun.com 更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;NotebookLM统计方法选择的核心挑战与认知重构 NotebookLM 作为 Google 推出的面向研究者与知识工作者的 AI 助手&#xff0c;其核心能力依赖于对用户上传…...

机器学习工作流编排利器:machiney-engine 轻量级流水线引擎详解

1. 项目概述与核心价值最近在GitHub上看到一个挺有意思的项目&#xff0c;叫Reidston/machiney-engine。光看名字&#xff0c;你可能会觉得这又是一个“机器学习引擎”或者“AI框架”&#xff0c;市面上这类项目多如牛毛&#xff0c;从TensorFlow、PyTorch这样的巨头&#xff0…...