c++,mutex,unique_lock,recursive_mutex,shared_mutex对比分析
当处理多线程并发时,正确使用锁是确保线程安全的关键。
1. std::mutex(互斥锁):
std::mutex
是C++标准库提供的最基本的锁。它的基本使用如下:
#include <iostream>
#include <mutex>
#include <thread>std::mutex myMutex;void sharedResourceAccess() {std::lock_guard<std::mutex> lock(myMutex);// 访问共享资源的代码std::cout << "Accessing shared resource...\n";
}int main() {std::thread t1(sharedResourceAccess);std::thread t2(sharedResourceAccess);t1.join();t2.join();return 0;
}
注意事项:
- 使用
std::lock_guard
是一种简单而安全的方式,它会在作用域结束时自动释放锁。 - 避免手动调用
unlock()
,因为忘记释放锁可能导致严重的问题。
2. std::unique_lock:
std::unique_lock
提供了更灵活的锁定和解锁方式,同时支持条件变量。在某些情况下,这种灵活性是很有用的:
#include <iostream>
#include <mutex>
#include <thread>std::mutex myMutex;void sharedResourceAccess() {std::unique_lock<std::mutex> lock(myMutex);// 访问共享资源的代码std::cout << "Accessing shared resource...\n";// lock.unlock(); // 可以手动解锁
}int main() {std::thread t1(sharedResourceAccess);std::thread t2(sharedResourceAccess);t1.join();t2.join();return 0;
}
注意事项:
std::unique_lock
可以在构造时不锁定,也可以手动解锁。- 支持条件变量,可以灵活地等待某个条件成立后再继续执行。
3. std::recursive_mutex:
std::recursive_mutex
允许同一个线程多次锁定同一把锁。这对于递归函数可能需要在同一线程中多次获取锁的情况很有用:
#include <iostream>
#include <mutex>
#include <thread>std::recursive_mutex myRecursiveMutex;void recursiveAccess(int depth) {std::unique_lock<std::recursive_mutex> lock(myRecursiveMutex);if (depth > 0) {recursiveAccess(depth - 1);}// 访问共享资源的代码std::cout << "Accessing shared resource at depth " << depth << "...\n";
}int main() {std::thread t1(recursiveAccess, 3);t1.join();return 0;
}
注意事项:
- 递归锁允许同一线程多次获取锁,但要小心不要导致死锁。
4. std::shared_mutex:
std::shared_mutex
是C++14标准引入的互斥锁,它提供了共享/独占两种锁定方式。这使得多个线程可以同时共享资源,而只有一个线程可以独占地修改资源。这在某些情况下能够提高并发性能。
以下是 std::shared_mutex
的主要特点和用法:
-
共享锁(Shared Lock):
- 多个线程可以同时获得共享锁,这允许它们并发地读取共享资源。
- 共享锁使用
std::shared_lock
来获取。
#include <shared_mutex>std::shared_mutex mySharedMutex;void readOperation() {std::shared_lock<std::shared_mutex> lock(mySharedMutex);// 读取共享资源的代码 }
-
独占锁(Exclusive Lock):
- 只有一个线程可以获得独占锁,这使得它能够独占地修改共享资源。
- 独占锁使用
std::unique_lock
来获取。
#include <shared_mutex>std::shared_mutex mySharedMutex;void writeOperation() {std::unique_lock<std::shared_mutex> lock(mySharedMutex);// 修改共享资源的代码 }
-
避免写者饥饿(Writer Starvation Avoidance):
std::shared_mutex
的设计旨在避免写者饥饿问题,即允许读者和写者以公平的方式争夺锁。
-
适用于读多写少的场景:
std::shared_mutex
在读多写少的情况下表现得较为优越,因为多个线程可以同时获得共享锁,提高了并发性能。
-
注意事项:
- 使用
std::shared_lock
进行读取操作,使用std::unique_lock
进行写入操作。 - 避免在写入操作中使用共享锁,以免破坏写者的互斥性。
- 使用
#include <iostream>
#include <shared_mutex>
#include <vector>
#include <thread>int sharedData=0;
std::shared_mutex mySharedMutex;void readOperation(int id) {//std::shared_lock<std::shared_mutex> lock(mySharedMutex);mySharedMutex.lock_shared();//手动控制// 读取共享资源的代码std::cout << "Reader " << id << " reading data: " << sharedData << std::endl;mySharedMutex.unlock_shared();//手动控制
}void writeOperation(int id) {//std::unique_lock<std::shared_mutex> lock(mySharedMutex);mySharedMutex.lock();//手动控制// 修改共享资源的代码sharedData=id;std::cout << "Writer " << id << " writing data." << std::endl;mySharedMutex.unlock();//手动控制
}int main() {std::vector<std::thread> readers;std::vector<std::thread> writers;for (int i = 0; i < 5; ++i) {readers.emplace_back(readOperation, i);writers.emplace_back(writeOperation, i);}for (auto& reader : readers) {reader.join();}for (auto& writer : writers) {writer.join();}return 0;
}
在这个示例中,读者和写者线程通过 std::shared_mutex
来协调对 sharedData
的读写操作。读者线程使用 std::shared_lock
获得共享锁,而写者线程使用 std::unique_lock
获得独占锁。这样,多个读者可以同时读取,而写者会独占地修改共享资源。
以下是对 std::mutex
,std::unique_lock
,std::recursive_mutex
,和 std::shared_mutex
的特点进行比较的表格:
特点 | std::mutex | std::unique_lock | std::recursive_mutex | std::shared_mutex |
---|---|---|---|---|
类型 | 互斥锁 | 可锁定、可解锁的锁 | 递归互斥锁 | 共享/独占互斥锁 |
RAII 风格 | 有(使用 std::lock_guard ) | 有 | 有 | 有(std::unique_lock ) |
支持条件变量 | 不支持 | 支持 | 不支持 | 支持 |
是否支持递归 | 不支持 | 不支持 | 支持 | 不支持 |
多线程性能 | 适用于大多数场景,较轻量级 | 较为灵活,适用于复杂的场景 | 适用于需要递归锁的场景 | 适用于读多写少的场景 |
锁定粒度 | 整个作用域内的代码 | 可以在较小的范围内进行锁定和解锁 | 整个作用域内的代码 | 可以同时支持独占和共享访问 |
死锁风险 | 高(如果未正确解锁,可能导致死锁) | 低(通过 std::lock() 可以避免死锁) | 递归锁允许同一线程多次获取锁,小心死锁风险 | 低(支持共享和独占访问,适当使用可以减少死锁风险) |
内存开销 | 低(较为轻量级) | 较高(提供了更多的功能) | 较高(需要额外的信息来支持递归锁) | 较高(需要维护更多状态信息) |
这个比较表格总结了这些锁的主要特点,但具体的选择取决于你的应用场景和需求。通常来说,std::mutex
是最基本的锁,而 std::unique_lock
提供了更多的灵活性,特别是在需要支持条件变量的情况下。std::recursive_mutex
对于需要在同一线程中多次获取锁的递归情况很有用。std::shared_mutex
则适用于读多写少的场景,提供了更好的并发性能。
相关文章:
c++,mutex,unique_lock,recursive_mutex,shared_mutex对比分析
当处理多线程并发时,正确使用锁是确保线程安全的关键。 1. std::mutex(互斥锁): std::mutex 是C标准库提供的最基本的锁。它的基本使用如下: #include <iostream> #include <mutex> #include <threa…...
MySQL与Oracle数据库在网络安全等级方面用到的命令
MySQL数据库命令集 查看数据库版本 SELECT VERSION(); 空口令查询 SELECT user,host,account_locked FROM mysql.user WHERE user ; SELECT * FROM mysql.user; 查询 用户的密码加密情况 SELECT HOST,USER,PLUGIN FROM mysql.user; 查询是否有空用户 SELECT host,user,plug…...

MySQL——视图
目录 一.视图介绍 二.基本使用 三.视图规则和限制 一.视图介绍 视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。视图的数据变化会影响到基表,基表的数据变化也会影响到视图。 二.基本使用 创…...

【响应式编程-03】Lambda表达式底层实现原理
一、简要描述 Lambda的底层实现原理Lambda表达式编译和运行过程 二、Lambda的底层实现原理 Lambda表达式的本质 函数式接口的匿名子类的匿名对象 反编译:cfr-0.145.jar 反编译:LambdaMetafactory.metafactory() 跟踪调试,转储Lambda类&#x…...

深入理解可变参数
1.C语言方式 目录 1.C语言方式 1.1.宏介绍 1.2.原理详解 1.3.宏的可变参数 1.4.案例分析 1.5.其他实例 2.C之std::initializer_list 2.1.简介 2.2.原理详解 2.3.案例分析 3.C之可变参数模版 3.1.简介 3.2.可变参数个数 3.3.递归包展开 3.4.逗号表达式展开 3.5…...
Centos7.9和Debian12部署Minio详细流程
一、安装minio Centos wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio-20230227181045.0.0.x86_64.rpm -O minio.rpm sudo dnf install minio.rpmDebian wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio_20230227181045.0…...

软件测试|教你如何使用UPDATE修改数据
简介 在SQL(Structured Query Language)中,UPDATE语句用于修改数据库表中的数据。通过UPDATE语句,我们可以更新表中的特定记录或多条记录,从而实现数据的修改和更新。本文将详细介绍SQL UPDATE语句的语法、用法以及一…...

新闻稿发布:媒体重要还是价格重要
在当今信息爆炸的数字时代,企业推广与品牌塑造不可或缺的一环就是新闻稿发布。新闻稿是一种通过媒体渠道传递企业信息、宣传品牌、事件或产品新闻的文本形式。发布新闻稿的过程旨在将企业的声音传递给更广泛的受众,借助媒体平台实现品牌故事的广泛传播。…...

prometheus grafana mysql监控配置使用
文章目录 前传bitnami/mysqld-exporter:0.15.1镜像出现了问题.my.cnf可以用这个"prom/mysqld-exporter:v0.15.0"镜像重要的事情mysql监控效果外传 前传 prometheus grafana的安装使用:https://nanxiang.blog.csdn.net/article/details/135384541 本文说…...

鸿蒙HarmonyOS-带笔锋手写板(三)
笔者用ArkTS 写了一个简单的带笔锋的手写板应用,并且可以将手写内容保存为图片。 一、效果图 手写效果如下(在鸿蒙手机模拟器上运行,手写时反应可能会有点慢) 二、实现方法 参考文章: 支持笔锋效果的手写签字控件_a…...
React 实现 Step组件
简介 本文将会实现步骤条组件功能。步骤条在以下几个方面改进。 1、将url与Step组件绑定,做到浏览器刷新,不会重定向到Step 1 2、通过LocalStorage 存储之前的Step,做到不丢失数据。 实现 Step.jsx (组件) import {useEffect, useState} fro…...

【OJ】单链表刷题
力扣刷题 1. 反转链表(206)1.1 题目描述1.2 题目分析1.2.1 头插法1.2.2 箭头反转 1.3 题目代码1.3.1 头插入1.3.2 箭头反转 2.合并两个有序链表(21)2.1 题目描述2.2 题目分析2.3 题目代码 1. 反转链表(206)…...

【UML建模】部署图(Deployment Diagram)
1.概述 部署图是一种结构图,用于描述软件系统在不同计算机硬件或设备上的部署和配置情况,以图形化的方式展示系统中组件、节点和连接之间的物理部署关系。 通过部署图,可以清晰地了解系统的物理结构和部署方式,包括系统组件和节…...
三、计算机理论-关系数据库-数据模型与数据视图;关系代数、关系演算及关系模型
数据模型 具体事物-抽象化-->概念模型-数据化-->数据模型 概念模型也称信息模型,在数据库设计阶段,由设计者按照用户的观点对数据和信息建模,实现对现实世界的概念抽象; 数据模型主要包括网状模型、层次模型、关系模型、面向…...

解读 $mash 通证 “Fair Launch” 规则(Staking 玩法解读篇)
Solmash 是 Solana 生态中由社区主导的铭文资产 LaunchPad 平台,该平台旨在为 Solana 原生铭文项目,以及通过其合作伙伴 SoBit 跨链桥桥接到 Solana 的 Bitcoin 生态铭文项目提供更广泛的启动机会。...
【C语言】关于C11的一些新特性
相比于VC 6.0使用的ANSI C标准,VS2022使用的C11标准与上一代有很多不同,相比之前的 C 标准(如 C89/C90 和 C99),引入了一些新的功能、特性和改进。以下是 C11 标准相对于之前版本的一些主要变化和新增内容:…...
牛的速记(c++题解)
题目描述 奶牛们误解了速记的含义。他们是这样理解的: 给出一个少于255个字母的小写字母串。 找到一个出现次数最多的字母,将该字母从字母串中统统删去,如果出现次数最多的字母不止一个,就删去在字母表中靠前的一个,即…...

使用ffmpeg+flv.js + websokect播放rtsp格式视频流
对于rtsp的视频流网上有很多种的解决方案,但是大的趋势还是利用ffmpeg的工具进行rtsp的视频解析进行一个推流,我最终选择bilibili开源的flv.js,代码十分的简单全部都在底层封装好了。实现的方式也比较容易理解,ffmpeg进行rtsp的视…...

OAI openair3代码结构整理
openair3代码框架结构 OAI(OpenAirInterface)是一个开源的5G网络软件平台,用于研究和开发5G网络技术。OpenAir3是OAI项目中的一个子项目,专注于5G核心网络的功能实现。 一、OpenAir3的代码主要包括以下几个部分: NAS…...
Kubernets(K8S)启动和运行 01-01 Kubernetes简介
Kubernets(K8S)启动和运行 01-01 Kubernetes简介 Kubernetes is an open source orchestrator for deploying containerized applications. It was originally developed by Google, inspired by a decade of experience deploying scalable, reliable systems in containers …...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...