C++(12): std::mutex及其高级变种的使用
1. 简述
在多线程或其他许多场景下,同时对一个变量或一段资源进行读写操作是一个比较常见的过程,保证数据的一致性和防止竞态条件至关重要。
C++的标准库中为我们提供了使用的互斥及锁对象,帮助我们实现资源的互斥操作。
2. std::mutex及其衍生互斥手段
(1)互斥类
std::mutex,最基本的 mutex 类。
std::recursive_mutex,递归 mutex 类。
std::time_mutex,定时 mutex 类。
std::recursive_timed_mutex,定时递归 mutex 类。
(2)RAII上锁
std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。
std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。
(3)API
std::try_lock,尝试上锁。如果当前互斥量已经被其他线程占用,当前线程不会阻塞,而是立即返回false。如果当前互斥量没有被其他线程占用,当前线程会获得该互斥量,完成上锁。需要注意的是,如果当前线程已经获得了该互斥量,那么再次进行try_lock就会造成死锁。
std::lock,上锁。调用该API会将互斥两上锁,如果当前互斥量已经被其他线程占用,则会阻塞,知道当前线程获得该锁。
std::unlock:解锁。
std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。
3. std::mutex使用
std::mutex是最简单的互斥量,可以单独使用为资源创建互斥环境,也可以与std::lock_guard合起来使用,实现一个RAII的应用。
需要注意的是,std::mutex仅支持一次加锁和解锁。如下是一个简单地小程序。
/** 引用头文件. */#include <mutex>/** 创建互斥量. */std::mutex mtx;/** 需要共享的某个资源 */int sharedResource;/** 操作上述共享资源的函数 */void accessResource() {/** 尝试加锁 */mtx.lock();/** 临界区开始 - 访问共享资源 */sharedResource += 1;/** 临界区结束 - 释放锁 */mtx.unlock();}
接下来是一个配合lock_guard使用的例程。
/** 引用头文件. */#include <mutex>/** 创建互斥量. */std::mutex mtx;/** 需要共享的某个资源 */int sharedResource;/** 操作上述共享资源的函数 */void accessResource() {/** 尝试加锁 */std::lock_guard<std::mutex> guard(mtx); // 自动加锁/** 临界区开始 - 访问共享资源 */sharedResource += 1;/** 退出函数,自动释放. */}
4. std::recursive_mutex递归锁
从名字可以看出,递归所是可以多次上锁的,当然也需要配合多次解锁,通常情况下也仅用在递归环境下。
如下是简单的使用std::recursive_mutex的示例。
#include <iostream>#include <thread>#include <mutex>std::recursive_mutex mtx;void func(int n) {mtx.lock();std::cout << "Thread " << n << " locked the mutex" << std::endl;if (n > 1) {func(n - 1);}std::cout << "Thread " << n << " unlocked the mutex" << std::endl;mtx.unlock();}int main() {std::thread t1(func, 3);std::thread t2(func, 2);t1.join();t2.join();return 0;}
如下是配合lock_guard使用的示例。
#include <iostream>#include <thread>#include <mutex>std::recursive_mutex mtx;void func(int n) {std::lock_guard<std::recursive_mutex> guard(mtx);std::cout << "Thread " << n << " locked the mutex" << std::endl;if (n > 1) {func(n - 1);}std::cout << "Thread " << n << " unlocked the mutex" << std::endl;}int main() {std::thread t1(func, 3);std::thread t2(func, 2);t1.join();t2.join();return 0;}
5. std::timed_mutex
std::timed_mutex 类似于 std::mutex,也是一个较为简单的锁。但是它额外提供了两个接口分别是 try_lock_for() 和 try_lock_until() 成员函数。前者允许线程尝试在一段时间内获取锁,如果在指定的时间内未能获得锁,线程将返回失败,并且可以根据返回值来判断是否继续等待或者执行其他逻辑。后者是一个确定的时间点,当到达指定的时间点以后,互斥锁不能够使用,则返回。
使用 std::timed_mutex 可以帮助避免线程因为获取锁时长时间阻塞而导致程序性能下降或死锁情况的发生。
6. std::lock_guard和std::unique_lock
std::lock_guard 和 std::unique_lock 都是 C++ 标准库中用于管理互斥量(mutex)的 RAII(Resource Acquisition Is Initialization,资源获取即初始化)包装器。它们都可以确保在持有互斥量的作用域内,互斥量会被安全地锁定和解锁,从而避免死锁和其他并发问题。不过,std::unique_lock 比 std::lock_guard 提供了更多的灵活性和功能。下面是它们的一些主要区别以及使用示例。
我们在前面第3节和第4节都列举了使用lock_guard的使用,lock_guard的优点是使用简单,缺点是过于简单了。
unique_lock能够实现和lock_guard一样的动能,也提供了更灵活的上锁和解锁控制。
unique_lock含有第二参数,如下所示:
std::adopt_lock :表示这个互斥量已经被lock了,你必须要把互斥量提前lock了,否则会报异常。std::adopt_lock标记的效果就是假设调用一方已经拥有了互斥量的所有权(也就是已经lock成功了),通知lock_guard和unique_lock不需要 再构造函数中lock这个互斥量了。
std::try_to_lock:我们会尝试用mutex的lock()去锁定这个mutex,但是如果没有锁定成功,也会立即返回,并不会阻塞在那里;使用这个try_to_lock的前提是你自己不能先lock。
std::defer_lock:不给mutex加锁,初始化了一个没有加锁的mutex。
前面讲到,unique_lock比lock_guard更为灵活,体现在哪里呢?事实上,unique_lock还拥有自己的成员函数,我们可以灵活的调用它的成员函数进行加解锁,而不是依赖于RAII。
unique_lock的成员函数如下。
lock:调用所管理的mutex对象的lock函数;
try_lock:调用所管理的mutex对象的try_lock函数;
try_lock_for:调用所管理的mutex对象的try_lock_for函数
try_lock_until:调用所管理的mutex对象的try_lock_until函数;
unlock:调用所管理的mutex对象的unlock函数;
release :返回所管理的mutex对象的指针,并释放所有权,但不改变mutex对象的状态;
owns_lock:返回当前std::unique_lock对象是否获得了锁;
mutex:返回当前std::unique_lock对象所管理的mutex对象的指针;
swap:交换两个unique_lock对象;
相关文章:
C++(12): std::mutex及其高级变种的使用
1. 简述 在多线程或其他许多场景下,同时对一个变量或一段资源进行读写操作是一个比较常见的过程,保证数据的一致性和防止竞态条件至关重要。 C的标准库中为我们提供了使用的互斥及锁对象,帮助我们实现资源的互斥操作。 2. std::mutex及其衍…...
基于ROS软路由的百元硬件升级方案实现突破千兆宽带
前言 很多用户得利于FTTR光网络不断推广,家用宽带带宽已经实现千兆速率的突破。而现在很多ISP运营商已经在多个城市率先推出2000M光宽带。这种情况下,要想将自家宽带的带宽能够充分发挥利用,就需要对原有的千兆设备进行升级来满足突破千兆的…...
OpenHarmony实战开发-分布式关系型数据库
介绍 本示例使用ohos.data.relationalStore 接口和ohos.distributedDeviceManager 接口展示了在eTS中分布式关系型数据库的使用,在增、删、改、查的基本操作外,还包括分布式数据库的数据同步同能。 效果预览 使用说明: 1.启动应用后点击“”按钮可以添…...
图片标注编辑平台搭建系列教程(6)——fabric渲染原理
原理 fabric的渲染步骤大致如下: 渲染前都设置背景图然后调用ctx.save(),存储画布的绘制状态参数然后调用每个object自身的渲染方法最后调用ctx.restore(),恢复画布的保存状态后处理,例如控制框的渲染等 值得注意的是࿰…...
Qt中QIcon图标设置(标题、菜单栏、工具栏、状态栏图标)
1 exe程序图标概述 在 Windows 操作系统中,程序图标一般会涉及三个地方; (1) 可执行程序(以及对应的快捷方式)的图标 (2) 程序界面标题栏图标 (3)程序在任务…...
C语言程序10题
第101题 (10.0分) 难度:易 第2章 /*------------------------------------------------------- 【程序填空】 --------------------------------------------------------- 功能:计算平均成绩并统计90分以上人数。 --…...
定时器-间歇函数
1.开启定时器 setInterval(function (){console.log(一秒执行一次)},1000) function fn(){console.log(一秒执行一次) } setInterval(fn,1000) //调用有名的函数,只写函数名 1.函数名字不需要加小括号 2.定时器返回是一个id数字 每个定时器的序号是不一样的 2.关…...
Ajax-XMLHttpRequest基本使用
一、Ajax的原理 就是XMLHttpRequest对象。 二、为什么学习XHR? 有更多与服务器数据通信方式,了解Ajax内部。 三、XHR使用步骤 1.创建XHR对象 2.调用open方法,设置url和请求方法 3.监听loadend事件,接受结果 4.调用send方法…...
门控循环单元(GRU)
概述 门控循环单元(Gated Recurrent Unit, GRU)由Junyoung Chung等人于2014年提出,原论文为《Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling》。GRU是循环神经网络(Recurrent Neural Network, …...
789. 数的范围 (二分学习)左端大右,右端小左
题目链接https://www.acwing.com/file_system/file/content/whole/index/content/4317/ 当求左端点时,条件是a【mid】大于等于x,并把右端点缩小。 当求右端点时,条件是a【mid】小于等于x,并把左端点扩大。 1.确定一个区间&…...
docker logs 查找日志常用命令
docker logs 是什么 docker logs 是 Docker 命令行工具提供的一个命令,用于查看容器的日志输出。它可以显示容器在运行过程中生成的标准输出(stdout)和标准错误输出(stderr),帮助用户诊断容器的行为和排查…...
百卓Smart管理平台 importexport.php SQL注入漏洞复现(CVE-2024-27718)
0x01 产品简介 百卓Smart管理平台是北京百卓网络技术有限公司(以下简称百卓网络)的一款安全网关产品,是一家致力于构建下一代安全互联网的高科技企业。 0x02 漏洞概述 百卓Smart管理平台 importexport.php 接口处存在SQL注入漏洞,攻击者除了可以利用 SQL 注入漏洞获取数据…...
PHP教程_PHP5函数str_replace替换字符串中的字符
PHP教程_PHP5函数str_replace替换字符串中的字符 PHP (PHP: Hypertext Preprocessor) 即 “超文本预处理器”, 是在服务器端执行的脚本语言, 尤其适用于Web开发并可嵌入HTML中。 PHP 语法学习了 C语言, 吸纳 Java 和 Perl 多个语言的特色发展出自己的特色语法, 并根据它们的长…...
Word的”交叉引用“和”插入题注“快捷键设置
Word的”交叉引用“和”插入题注“快捷键设置 在MSWord2021中,可以自定义设置快捷键。方法如下:文件-选项-自定义功能区-键盘快捷方式(自定义)。具体过程如图所示。 最后,按照上述流程将插入题注(Insert…...
小白从0学习ctf(web安全)
文章目录 前言一、baby lfi(bugku-CTF)1、简介2、解题思路1、解题前置知识点2、漏洞利用 二、baby lfi 2(bugku-CTF)1.解题思路1、漏洞利用 三、lfi(bugku CTF)1、解题思路1、漏洞利用 总结 前言 此文章是…...
【嵌入式开发 Linux 常用命令系列 7.4 -- awk 处理文件名,去除后缀只保留文件名】
请阅读【嵌入式开发学习必备专栏 】 文章目录 awk 处理文件名,去除后缀只保留文件名 awk 处理文件名,去除后缀只保留文件名 在 shell 中, 可以使用 awk 来处理文件名,去除其后缀。下面是一个示例命令,它会将带有后缀的…...
Linux重点思考(中)--端口/静态内存/负载/日志
这里写目录标题 知道的linux常用命令:查看指定端口进程netstat -pantunetstat -pantu|grep 22 静态运行内存free硬盘物理内存df和du当前负载uptime查看日志awk统计文件每一行单词sed 替换文件单词 知道的linux常用命令:查看指定端口进程 netstat -pantu…...
【Go】五、流程控制
文章目录 1、if2、switch3、for4、for range5、break6、continue7、goto8、return 1、if 条件表达式左右的()是建议省略的if后面一定要有空格,和条件表达式分隔开来{ }一定不能省略if后面可以并列的加入变量的定义 if count : 20;count < 30 {fmt.Println(&quo…...
数据开发-面试真题。
1. 自我介绍 2.在培训班的学过的项目经历 3.之前的工作经历,以及薪资 4.开始讲之前的项目经历 5.技术面试官开始提问。 kafka中进行数据分层,怎么从kafka中实时查询到相关的数据,一条或几条 6.java中的集合,以及io流 7.给定…...
如何使用免费的ChatGpt3.5
如何使用免费的ChatGpt 最近免费的gpt3.5很多都不怎么行了实在是太给力了尾声 最近免费的gpt3.5很多都不怎么行了 原因是什么呢?因为openai已经取消了免费的5刀赠送,那么这些人手上的免费的sses-key 用完后,就基本上全军覆没了,再…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...
李沐--动手学深度学习--GRU
1.GRU从零开始实现 #9.1.2GRU从零开始实现 import torch from torch import nn from d2l import torch as d2l#首先读取 8.5节中使用的时间机器数据集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps) #初始化模型参数 def …...
LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
目录 lua脚本 记录流水 记录流水的作用 流水什么时候删除 我们在做库存扣减的时候,显示基于Lua脚本和Redis实现的预扣减 这样可以在秒杀扣减的时候保证操作的原子性和高效性 lua脚本 // ... 已有代码 ...Overridepublic InventoryResponse decrease(Inventor…...
第14节 Node.js 全局对象
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。 在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局…...
Oracle 19c RAC集群ADG搭建
1、将主库的pfile和passwdfile发送到备库 #主库一节点操作 scp -P1234 /tmp/pfile2025.ora bak_ip:/home/oracle sco -P1234 /oracle/app/oracle/product/19.0.0/db/dbs/orapw$ORACLE_SID bak_ip:/oracle/app/oracle/product/19.0.0/db/dbs 2、备库修改参数文件成standby相关…...
C++.OpenGL (4/64)纹理(Texture)
纹理(Texture) 纹理映射核心流程 #mermaid-svg-XxVbt4fizulzb5H3 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-XxVbt4fizulzb5H3 .error-icon{fill:#552222;}#mermaid-svg-XxVbt4fizulzb5H3 .error-text{fill:…...
系统模块与功能设计框架
系统模块与功能设计框架,严格遵循专业架构设计原则,基于行业标准(如微服务架构、DDD领域驱动设计)构建。设计采用分层解耦模式,确保可扩展性和可维护性,适用于电商、企业服务、数字平台等中大型系统。 系统…...
