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

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...