一文讲通锁标记对象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] 锁标记对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/73349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LVI-SAM、VINS-Mono、LIO-SAM算法的阅读参考和m2dgr数据集上的复现(留作学习使用)

ROS一键安装参考&#xff1a; ROS的最简单安装——鱼香一键安装_鱼香ros一键安装-CSDN博客 opencv官网下载4.2.0参考&#xff1a;https://opencv.org/releases/page/3/ nvidia驱动安装:ubuntu18.04 安装显卡驱动 - 开始战斗 - 博客园 cuda搭配使用12 cuda安装1&#xff1a;Ub…

基于jspm校园安全管理系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 随着信息时代的来临&#xff0c;过去信息校园安全管理方式的缺点逐渐暴露&#xff0c;本次对过去的校园安全管理方式的缺点进行分析&#xff0c;采取计算机方式构建校园安全管理系统。本文通过阅读相关文献&#xff0c;研究国内外相关技术&#xff0c;提出了一种集安全教…

基于NXP+FPGA轨道交通3U机箱结构牵引控制单元

基于NXPFPGA轨道交通异步电机牵引控制单元(TCU-IM) 异步电机牵引控制单元&#xff08;TCU-IM&#xff09;用于牵引逆变器-异步电机构成的牵引电传动系统&#xff0c;可采用车控或架控方式。执行高性能异步电机复矢量控制策略&#xff0c;具有响应迅速、有效可靠的防空转滑行控制…

《CircleCI:CircleCI:解锁软件开发持续集成(CI)和持续部署(CD)高效密码》:此文为AI自动生成

《CircleCI&#xff1a;CircleCI&#xff1a;解锁软件开发持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;高效密码》&#xff1a;此文为AI自动生成 一、CircleCI 初印象 在当今软件开发的快节奏赛道上&#xff0c;持续集成&#xff08;CI&#xff…

基于MySQL有用户管理的音乐播放器

基于MySQL的音乐器 带有用户登录功能验证用户身份&#xff0c;用户注册等操作还有用户音乐列表&#xff0c;以及增删查改操作 INSERT into users(username,passwd,phone_number,created_time,role) VALUES(‘张三’,‘123456’,‘123’,‘2025-3-11’,‘1’) 三张表&#xf…

差分专题练习 ——基于罗勇军老师的《蓝桥杯算法入门C/C++》

一、1.重新排序 - 蓝桥云课 算法代码&#xff1a; #include <bits/stdc.h> using namespace std; const int N 1e5 3;int a[N], d[N], cnt[N];int main() {int n; scanf("%d", &n);for (int i 1; i < n; i) scanf("%d", &a[i]);int m…

AI+视频监控电力巡检:EasyCVR视频中台方案如何赋能电力行业智能化转型

随着电力行业的快速发展&#xff0c;电力设施的安全性、稳定性和运维效率变得至关重要。传统视频监控系统在实时性、智能化及多系统协同等方面面临严峻挑战。EasyCVR视频中台解决方案作为一种先进的技术手段&#xff0c;在电力行业中得到了广泛应用&#xff0c;为电力设施的监控…

【哈希表与字符串的算法之路:思路与实现】—— LeetCode

文章目录 两数之和面试题01.02.判定是否为字符重排存在重复元素存在重复元素||字母异位词分组最长公共前缀和最长回文子串二进制求和字符串相乘 两数之和 这题的思路很简单&#xff0c;在读完题目之后&#xff0c;便可以想到暴力枚举&#xff0c;直接遍历整个数组两遍即可&…

RabbitMQ入门:从安装到高级消息模式

文章目录 一. RabbitMQ概述1.1 同步/异步1.1.1 同步调用1.1.2 异步调用 1.2 消息中间件1.2.1 概念1.2.2 作用1.2.3 常见的消息中间件1.2.4 其他中间件 1.3 RabbitMQ1.3.1 简介1.3.2 特点1.3.3 方式1.3.4 架构1.3.5 运行流程 二. 安装2.1 Docker 安装 RabbitMQ 三. 简单队列&…

kernel与modules解耦

一、耦合&#xff1a; linux的kernel与modules存在耦合版本匹配&#xff0c;在版本不匹配&#xff08;内核重新编译后&#xff0c;或者驱动模块编译依赖的内核版本跟运行版本不匹配&#xff09;时候&#xff0c;会存在insmod 驱动模块失败的情形&#xff1b; 二、解耦&#xff…

物理约束神经网络(PINN)和有限元方法哪个更接近“真正的物理规律”?还是两者只是不同的数学表达?

物理约束神经网络(Physics-Informed Neural Networks, PINN)和有限元方法(Finite Element Method, FEM)是两种在科学计算和工程模拟中广泛应用的数值方法。PINN 依赖深度学习来近似微分方程的解,并在训练过程中将物理约束作为损失项融入网络,而 FEM 通过将连续介质的物理…

UI程序的std::cout重定向输出到Visual Studio的debug输出窗口

常用代码。 UI程序的std::cout重定向输出到Visual Studio的debug输出窗口 #include <iostream> #include <streambuf> #include <vector> #include <string> #include <afxwin.h> //MFC// 自定义 streambuf 类&#xff0c;用于重定向输出到 Vis…

Python开发合并多个PDF文件

前言 在我们的工作中&#xff0c;可能有以下场景需要用到合并多个PDF&#xff1a; 文档归档&#xff1a;在企业或组织中&#xff0c;常常需要将相关的文档&#xff08;如合同、报告、发票等&#xff09;合并为一个PDF文件&#xff0c;以便于归档和管理。 报告生成&#xff1a;在…

DeepSeek 助力 C++ 开发:探索智能编程新境界

这篇文章就会详细讲讲 DeepSeek 在 C 开发里到底能怎么用&#xff0c;从上面说的写代码、找错误、优化性能&#xff0c;到管理项目这些方面&#xff0c;还会给出好多实际的代码例子&#xff0c;讲讲实际用起来是啥情况。目的就是给那些做 C 开发的人&#xff0c;一份全面又详细…

C#-使用VisualStudio编译C#工程

一.创建csproj文件 二.创建源cs文件 三.生成解决方案 四.运行解决方案 五.VisualStudio功能列表 <1.代码格式化: CtrlKD完成代码整体格式化 <2.窗口布局 窗口->重置窗口布局 <3.引用查找&关联 <4.包管理 <5.日志输出级别 工具->选项->项目解决方案…

Kafka相关的面试题

以下是150道Kafka相关的面试题及简洁回答&#xff1a; Kafka基础概念 1. 什么是Kafka&#xff1f; Kafka是一个分布式、可扩展、容错的发布-订阅消息系统&#xff0c;最初由LinkedIn开发&#xff0c;现为Apache项目。它适用于高吞吐量的场景&#xff0c;如大数据处理和实时数据…

CTF--Web安全--SQL注入之报错注入

CTF–Web安全–SQL注入之报错注入 一、报错注入的概念 用户使用数据库查询语句&#xff0c;向数据库发送错误指令&#xff0c;数据库返回报错信息&#xff0c;报错信息中参杂着我们想要获取的隐私数据。通常在我们在页面显示中找不到回显位的时候&#xff0c;使用报错注入。 二…

深度学习中学习率调整策略

学习率衰减策略是深度学习优化过程中的一个关键因素&#xff0c;它决定了训练过程中学习率的调整方式&#xff0c;从而影响模型收敛的速度和效果。不同的衰减策略在不同的任务和模型上可能有不同的表现&#xff0c;下面从我用到过的几个衰减策略进行记录&#xff0c;后续慢慢跟…

JavaCV

调用摄像头 public class Camera {public static void main(String[] args) throws FrameGrabber.Exception {// 开启抓取器OpenCVFrameGrabber grabber new OpenCVFrameGrabber(0);grabber.start();// 开启窗口CanvasFrame canvasFrame new CanvasFrame("OpenCV Frame…

凝思linux修改mac地址

临时性修改 /sbin/ifconfig eth0 hw ether 00:0C:29:36:97:20