当前位置: 首页 > 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…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...