一文讲通锁标记对象std::adopt_lock盲点
一文讲通锁标记对象std::adopt_lock盲点
- 1. 核心概念
- 2. 代码详解
- 1. 单个锁
- 2. 多重锁(可以用来预防死锁)
- 3. 条件变量的互斥控制
- 4. 复杂示例: 多生产者-多消费者模型(超纲了, 可不看,哈哈哈哈)
- 3. 小结
1. 核心概念
在C++中, std::adopt_lock是一个锁标记对象[^1], 用于配合锁对象(如 std::lock_guard、std::unique_lock 或 std::shared_lock)来管理互斥锁(mutex), 它的作用是告诉锁对象:互斥锁已经被手动锁定了,锁对象只需要“接管”这个锁的所有权,而不需要再次尝试锁定。
2. 代码详解
1. 单个锁
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx; // Global mutexvoid process() {// Step 1: Manually lock the mutex.mtx.lock(); // The current thread now owns the mutex.// Step 2: Construct a lock_guard to adopt the existing lock.// The std::adopt_lock tag tells lock_guard that mtx is already locked.std::lock_guard<std::mutex> guard(mtx, std::adopt_lock);// Critical section: safe access to shared resources.std::cout << "Inside critical section." << std::endl;// When 'guard' goes out of scope, its destructor will call mtx.unlock().
}int main() {std::thread t(process);t.join(); // Wait for the thread to finish.return 0;
}
这里的关键点是 std::adopt_lock,它告诉 lock_guard 互斥量已经被锁定,因此 lock_guard 不会尝试再次调用
mtx.lock()。
如果没有使用 std::adopt_lock,std::lock_guard 会在构造时试图锁定互斥量,这将导致死锁。
2. 多重锁(可以用来预防死锁)
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx1;
std::mutex mtx2;void task() {// Step 1: Atomically lock both mutexes using std::lock, which prevents deadlock.std::lock(mtx1, mtx2);// Step 2: Create RAII objects that adopt these locks.std::lock_guard<std::mutex> guard1(mtx1, std::adopt_lock);std::lock_guard<std::mutex> guard2(mtx2, std::adopt_lock);// Critical section: safe access to shared resources protected by mtx1 and mtx2.std::cout << "Thread safely acquired both mutexes." << std::endl;// Both mutexes will be unlocked when guard1 and guard2 go out of scope.
}int main() {std::thread t1(task);std::thread t2(task);t1.join();t2.join();return 0;
}
3. 条件变量的互斥控制
在条件变量中结合 std::unique_lock 和 std::adopt_lock 使用,可以灵活地管理锁的状态。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void workerThread() {// 手动锁定互斥量mtx.lock();std::cout << "Worker thread acquired lock manually.\n";// 使用 adopt_lock 转移锁管理权std::unique_lock<std::mutex> lock(mtx, std::adopt_lock);// 等待条件变量被通知cv.wait(lock, [] { return ready; });std::cout << "Worker thread is processing data.\n";
}void notifierThread() {std::this_thread::sleep_for(std::chrono::seconds(1));// 通知条件变量{std::lock_guard<std::mutex> lock(mtx); // 自动管理锁ready = true;std::cout << "Notifier thread is notifying.\n";}cv.notify_one();
}int main() {std::thread worker(workerThread);std::thread notifier(notifierThread);worker.join();notifier.join();return 0;
}
解释补充
- worker_thread 手动锁定互斥量并通过 std::adopt_lock 转移锁管理权给 std::unique_lock。
- notifier_thread 通过 std::lock_guard 安全通知条件变量。
4. 复杂示例: 多生产者-多消费者模型(超纲了, 可不看,哈哈哈哈)
假设有多个生产者线程向共享队列中添加任务,而多个消费者线程从队列中处理任务。为了避免死锁,使用 std::adopt_lock 配合多个互斥量和条件变量。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <chrono>std::mutex queue_mutex; // 队列的互斥量
std::mutex print_mutex; // 打印输出的互斥量
std::condition_variable cv; // 条件变量
std::queue<int> task_queue; // 共享任务队列
const int MAX_QUEUE_SIZE = 10; // 队列的最大容量
bool stop = false; // 用于通知消费者线程停止// 生产者函数
void producer(int id) {for (int i = 0; i < 20; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟生产延迟std::unique_lock<std::mutex> lock(queue_mutex); // 锁定队列的互斥量cv.wait(lock, [] { return task_queue.size() < MAX_QUEUE_SIZE; }); // 等待队列有空间task_queue.push(i);{// 使用 adopt_lock 安全打印日志std::lock_guard<std::mutex> print_lock(print_mutex, std::adopt_lock);std::cout << "Producer " << id << " produced task " << i << ". Queue size: " << task_queue.size() << std::endl;}cv.notify_all(); // 通知消费者}
}// 消费者函数
void consumer(int id) {while (true) {std::unique_lock<std::mutex> lock(queue_mutex);cv.wait(lock, [] { return !task_queue.empty() || stop; }); // 等待队列有任务或停止信号if (stop && task_queue.empty()) break; // 如果停止并且队列为空,退出int task = task_queue.front();task_queue.pop();{// 使用 adopt_lock 安全打印日志std::lock_guard<std::mutex> print_lock(print_mutex, std::adopt_lock);std::cout << "Consumer " << id << " consumed task " << task << ". Queue size: " << task_queue.size() << std::endl;}cv.notify_all(); // 通知生产者}
}int main() {std::vector<std::thread> producers;std::vector<std::thread> consumers;// 启动生产者线程for (int i = 0; i < 3; ++i) {producers.emplace_back(producer, i + 1);}// 启动消费者线程for (int i = 0; i < 2; ++i) {consumers.emplace_back(consumer, i + 1);}// 等待所有生产者完成for (auto& p : producers) {p.join();}// 通知消费者停止{std::lock_guard<std::mutex> lock(queue_mutex);stop = true;}cv.notify_all();// 等待所有消费者完成for (auto& c : consumers) {c.join();}std::cout << "All tasks processed. Exiting program." << std::endl;return 0;
}
3. 小结
std::adopt_lock 适用于以下场景:
- Lock First, Adopt Later:手动锁定互斥量后,将管理权交给锁管理类(如 std::lock_guard 或 std::unique_lock)。
- 注释要清晰(就是这么突兀! 没错, 就是没有这个场景!!!, 想想我为什么要写?还标红加粗)
- 多个互斥量的协调锁定。
- 配合条件变量灵活管理锁定逻辑。
[^1] 锁标记对象
相关文章:
一文讲通锁标记对象std::adopt_lock盲点
一文讲通锁标记对象std::adopt_lock盲点 1. 核心概念2. 代码详解1. 单个锁2. 多重锁(可以用来预防死锁)3. 条件变量的互斥控制4. 复杂示例: 多生产者-多消费者模型(超纲了, 可不看,哈哈哈哈) 3. 小结 1. 核心概念 在C中, std::adopt_lock是一…...
Vscode工具开发Vue+ts项目时vue文件ts语法报错-红波浪线等
Vscode工具开发Vuets项目时vue文件ts语法报错-红波浪线等 解决方案 问题如题描述,主要原因是开发工具使用的代码检查与项目的中的ts不一致导导致,解决办法,修改 vscode 中, 快捷键:command shift p, 输入ÿ…...
Mac下安装Zed以及Zed对MCP(模型上下文协议)的支持
Zed是当前新流行的一种编辑器,支持MCP(模型上下文协议) Mac下安装Zed比较简单,直接有安装包,在这里: brew install --cask zedMac Monterey下是可以安装上的,亲测有效。 配置 使用CtrlShiftP…...
ROS实践(五)机器人自动导航(robot_navigation)
目录 一、知识点 1. 定位 2. 路径规划 (1)全局路径规划 (2)局部路径规划 3. 避障 二、常用工具和传感器 三、相关功能包 1. move_base(决策规划) 2. amcl(定位) 3. costmap_2d(代价地图) 4. global_planner(全局规划器) 5. local_planner(局部规划器…...
REDIS生产环境配置
REDIS生产环境配置 REDIS生产环境配置docker-compose文件redis.conf文件 REDIS生产环境配置 docker-compose模式部署生产环境 docker-compose文件 d_redis:image: redis:${REDIS_VERSION}container_name: d_redisvolumes:- ${REDIS_1_CONF_FILE}:/etc/redis.conf:ro- ${DATA_…...
【小沐学Web3D】three.js 加载三维模型(React)
文章目录 1、简介1.1 three.js1.2 react.js 2、three.js React结语 1、简介 1.1 three.js Three.js 是一款 webGL(3D绘图标准)引擎,可以运行于所有支持 webGL 的浏览器。Three.js 封装了 webGL 底层的 API ,为我们提供了高级的…...
软考教材重点内容 信息安全工程师 第19章 操作系统安全保护
19.1.1 操作系统安全概念 一般来说,操作系统的安全是指满足安全策略要求,具有相应的安全机制及安全功能,符合特定的安全标准,在一定约束条件下,能够抵御常见的网络安全威胁,保障自身的安全运行及资源安全。…...
【C++设计模式】第二十一篇:模板方法模式(Template Method)
注意:复现代码时,确保 VS2022 使用 C17/20 标准以支持现代特性。 算法骨架的标准化定义 1. 模式定义与用途 核心思想 模板方法模式:在父类中定义算法的骨架,将某些步骤延迟到子类实现,使得子类不改变算法结构即可…...
【机器学习】基于t-SNE的MNIST数据集可视化探索
一、前言 在机器学习和数据科学领域,高维数据的可视化是一个极具挑战但又至关重要的问题。高维数据难以直观地理解和分析,而有效的可视化方法能够帮助我们发现数据中的潜在结构、模式和关系。本文以经典的MNIST手写数字数据集为例,探讨如何利…...
【Pycharm】Pycharm无法复制粘贴,提示系统剪贴板不可用
我也没有用vim的插件,检查了本地和ubutnu上都没有。区别是我是远程到ubutnu的pycharm,我本地直接控制windowes的pycharm是没问题的。现象是可以从外部复制到pycharm反之则不行。 ctl c ctlv 以及右键 都不行 参考:Pycharm无法复制粘贴&…...
基于python+django+vue.js开发的医院门诊管理系统/医疗管理系统源码+运行
功能介绍 平台采用B/S结构,后端采用主流的Python语言进行开发,前端采用主流的Vue.js进行开发。源码 功能包括:医生管理、科室管理、护士管理、住院管理、药品管理、用户管理、日志管理、系统信息模块。 源码地址 https://github.com/geee…...
Spring Boot整合RabbitMQ极简教程
一、消息队列能解决什么问题? 异步处理:解耦耗时操作(如发短信、日志记录)流量削峰:应对突发请求,避免系统过载应用解耦:服务间通过消息通信,降低依赖 二、快速整合RabbitMQ 1. 环…...
Flink-学习路线
最近想学习一下Flink,公司的实时需求还是不少的,因此结合ai整理了一份学习路线,记录一下。 当然,公司也有Scala版本Flink框架,也学习了一下。这里只说Java版本 1. Java基础 目标: 掌握Java编程语言的基础知识。 内容…...
Atcoder ABC397-D 题解
https://atcoder.jp/contests/abc397/tasks/abc397_dhttps://atcoder.jp/contests/abc397/tasks/abc397_d 题目描述: 确定是否存在一对正整数,使得 思路: 首先对方程进行转化 设 即 接下来确定的范围 根据立方差公式 因此,我们可以从到来…...
K8S学习之基础二十七:k8s中daemonset控制器
k8s中DaemonSet控制器 DaemonSet控制器确保k8s集群中,所有节点都运行一个相同的pod,当node节点增加时,新节点也会自动创建一个pod,当node节点从集群移除,对应的pod也会自动删除。删除DaemonSet也会删除创建的pod。…...
电机控制常见面试问题(八)
文章目录 一、解释什么是矢量控制及其优势二、 如何设计一个电机控制系统的开环和闭环控制?请给出具体案例三.如何通过软件模拟来优化电机控制设计四. 请解释电机过流保护过压保护过温保护等保护机制五.你熟悉哪些电机驱动拓扑结构六.解释空间适量脉宽调制的原理 一…...
保持docker内容器一直运行
首先:确保Docker服务配置为开机自启,这样当虚拟机启动时,Docker也会启动,并按照设定的重启策略自动启动相关容器。 sudo systemctl enable docker 创建容器时: 当你使用docker run命令启动容器时,可以添…...
神经网络的基本知识
感知机 输入:来自其他 n 个神经元传递过来的输入信号 处理:输入信号通过带权重的连接进行传递, 神经元接受到总输入值将与神经元的阈值进行比较 输出:通过激活函数的处理以得到输出 感知机由两层神经元组成, 输入层接受外界输入信号传递给…...
PostgreSQL技术内幕26:PG聚合算子实现分析
文章目录 0.简介1.概念说明2.朴素聚集3.Group by聚集3.1 哈希聚集3.2 分组聚集 0.简介 聚合算子在聚合函数在数据分析、报告生成和统计计算中扮演着重要角色,通过对多行数据进行计算,将多个输入值压缩为单一输出值,如求和、平均值、计数等。…...
【RS】OneRec快手-生成式推荐模型
note 本文提出了一种名为 OneRec 的统一生成式推荐框架,旨在替代传统的多阶段排序策略,通过一个端到端的生成模型直接生成推荐结果。OneRec 的主要贡献包括: 编码器-解码器结构:采用稀疏混合专家(MoE)架构…...
AVL树的平衡算法的简化问题
AVL树是一种紧凑的二叉查找树。它的每个结点,都有左右子树高度相等,或者只相差1这样的特性。文章https://blog.csdn.net/aaasssdddd96/article/details/106291144给出了一个例子。 为了便于讨论,这里对AVL树的结点平衡情况定义2个名称&#…...
mac安装navicat及使用
0.删除旧的 sudo rm -Rf /Applications/Navicat\ Premium.app sudo rm -Rf /private/var/db/BootCaches/CB6F12B3-2C14-461E-B5A7-A8621B7FF130/app.com.prect.NavicatPremium.playlist sudo rm -Rf ~/Library/Caches/com.apple.helpd/SDMHelpData/Other/English/HelpSDMIndexF…...
【HTML】二、列表、表格
文章目录 1、列表1.1 无序列表1.2 有序列表1.3 定义列表 2、表格2.1 定义2.2 表格结构标签2.3 合并单元格 1、列表 列表分为: 无序列表有序列表定义列表:一个标题下有多个小分类 1.1 无序列表 ul嵌套li,ul是无序列表,li是列表…...
大语言模型安全风险分析及相关解决方案
大语言模型的安全风险可以从多个维度进行分类。 从输入输出的角度来看,存在提示注入、不安全输出处理、恶意内容生成和幻觉错误等风险; 从数据层面来看,训练数据中毒、敏感信息泄露和模型反演攻击是主要威胁; 模型自身则面临拒绝服务和盗窃的风险; 供应链和插件的不安全引…...
windows平台的ffmpeg编译使用
windows平台的ffmpeg编译使用 一、现状 本人使用libgdx开发galGame,发现扩展包gdx-video不支持mp4,不能忍,正好看到官网有支持自定义编译的文档,所以操作一下,自定义编译。本文重点在于操作windows平台,linux平台太简单了。 整个过程包括如下几个步骤。 二、代码下载…...
FFMPEG录制远程监控摄像头MP4
手绘效果图 上图是录制功能的HTML前端页面,录制功能和解码视频放在一起。录制功能关键是录制(开始录制按钮)、停止录像按钮。当点击“录制”的时候则会开始录制MP4文件, 当点击停止的时候就会停止录制MP4。经过录制后,则会生成MP4,并放到我的RV1126的/tm…...
centos操作系统上传和下载百度网盘内容
探序基因 整理 进入百度网盘官网百度网盘 客户端下载 下载linux的rpm格式的安装包 在linux命令行中输入:rpm -ivh baidunetdisk_4.17.7_x86_64.rpm 出现报错: 错误:依赖检测失败: libXScrnSaver 被 baidunetdisk-4.17.7-1.x8…...
Rubick:基于 Electron 的开源插件化桌面效率工具箱
Rubick 是一款基于 Electron 构建的开源桌面工具箱,专为追求高效办公和个性化体验的用户设计。它通过自由集成丰富的插件,让用户能够根据自己的需求打造极致的桌面端效率工具。 软件命名由来Rubick 的名字来源于《DOTA2》中的英雄 Rubick(拉…...
ruoyi-vue部署
ruoyi源码类型 Ruoyi源码 编译打包后,直接部署tomcat服务器 Ruoyi-vue 前后端分离版 前端部署到nginx 后端部署到tomcat RuoYi-Cloud 微服务版 RuoYi-app 移动端版 RuoYi-vue 前后端分离版 环境 JDK>=1.8 MySQL >= 5.7 Maven >= 3.0 Node >= 12 Redis…...
MyBatis 如何创建 SqlSession 对象的?
MyBatis 创建 SqlSession 对象的过程主要由 SqlSessionFactory 接口及其实现类来完成。以下是详细步骤: 1. SqlSessionFactory 接口: SqlSessionFactory 是 MyBatis 的核心接口之一,它负责创建 SqlSession 对象。 你可以将 SqlSessionFactory 视为 Sql…...
