一文讲通锁标记对象std::adopt_lock盲点

news2025/3/16 23:24:15

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

void 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.coloradmin.cn/o/2316294.html

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

相关文章

Vscode工具开发Vue+ts项目时vue文件ts语法报错-红波浪线等

Vscode工具开发Vuets项目时vue文件ts语法报错-红波浪线等 解决方案 问题如题描述&#xff0c;主要原因是开发工具使用的代码检查与项目的中的ts不一致导导致&#xff0c;解决办法&#xff0c;修改 vscode 中&#xff0c; 快捷键&#xff1a;command shift p, 输入&#xff…

ROS实践(五)机器人自动导航(robot_navigation)

目录 一、知识点 1. 定位 2. 路径规划 (1)全局路径规划 (2)局部路径规划 3. 避障 二、常用工具和传感器 三、相关功能包 1. move_base(决策规划) 2. amcl(定位) 3. costmap_2d(代价地图) 4. global_planner(全局规划器) 5. local_planner(局部规划器…

【小沐学Web3D】three.js 加载三维模型(React)

文章目录 1、简介1.1 three.js1.2 react.js 2、three.js React结语 1、简介 1.1 three.js Three.js 是一款 webGL&#xff08;3D绘图标准&#xff09;引擎&#xff0c;可以运行于所有支持 webGL 的浏览器。Three.js 封装了 webGL 底层的 API &#xff0c;为我们提供了高级的…

软考教材重点内容 信息安全工程师 第19章 操作系统安全保护

19.1.1 操作系统安全概念 一般来说&#xff0c;操作系统的安全是指满足安全策略要求&#xff0c;具有相应的安全机制及安全功能&#xff0c;符合特定的安全标准&#xff0c;在一定约束条件下&#xff0c;能够抵御常见的网络安全威胁&#xff0c;保障自身的安全运行及资源安全。…

【机器学习】基于t-SNE的MNIST数据集可视化探索

一、前言 在机器学习和数据科学领域&#xff0c;高维数据的可视化是一个极具挑战但又至关重要的问题。高维数据难以直观地理解和分析&#xff0c;而有效的可视化方法能够帮助我们发现数据中的潜在结构、模式和关系。本文以经典的MNIST手写数字数据集为例&#xff0c;探讨如何利…

【Pycharm】Pycharm无法复制粘贴,提示系统剪贴板不可用

我也没有用vim的插件&#xff0c;检查了本地和ubutnu上都没有。区别是我是远程到ubutnu的pycharm&#xff0c;我本地直接控制windowes的pycharm是没问题的。现象是可以从外部复制到pycharm反之则不行。 ctl c ctlv 以及右键 都不行 参考&#xff1a;Pycharm无法复制粘贴&…

Flink-学习路线

最近想学习一下Flink&#xff0c;公司的实时需求还是不少的&#xff0c;因此结合ai整理了一份学习路线&#xff0c;记录一下。 当然&#xff0c;公司也有Scala版本Flink框架&#xff0c;也学习了一下。这里只说Java版本 1. Java基础 目标: 掌握Java编程语言的基础知识。 内容…

Atcoder ABC397-D 题解

https://atcoder.jp/contests/abc397/tasks/abc397_dhttps://atcoder.jp/contests/abc397/tasks/abc397_d 题目描述&#xff1a; 确定是否存在一对正整数,使得 思路&#xff1a; 首先对方程进行转化 设 即 接下来确定的范围 根据立方差公式 因此&#xff0c;我们可以从到来…

K8S学习之基础二十七:k8s中daemonset控制器

k8s中DaemonSet控制器 ​ DaemonSet控制器确保k8s集群中&#xff0c;所有节点都运行一个相同的pod&#xff0c;当node节点增加时&#xff0c;新节点也会自动创建一个pod&#xff0c;当node节点从集群移除&#xff0c;对应的pod也会自动删除。删除DaemonSet也会删除创建的pod。…

神经网络的基本知识

感知机 输入&#xff1a;来自其他 n 个神经元传递过来的输入信号 处理&#xff1a;输入信号通过带权重的连接进行传递, 神经元接受到总输入值将与神经元的阈值进行比较 输出&#xff1a;通过激活函数的处理以得到输出 感知机由两层神经元组成, 输入层接受外界输入信号传递给…

PostgreSQL技术内幕26:PG聚合算子实现分析

文章目录 0.简介1.概念说明2.朴素聚集3.Group by聚集3.1 哈希聚集3.2 分组聚集 0.简介 聚合算子在聚合函数在数据分析、报告生成和统计计算中扮演着重要角色&#xff0c;通过对多行数据进行计算&#xff0c;将多个输入值压缩为单一输出值&#xff0c;如求和、平均值、计数等。…

【RS】OneRec快手-生成式推荐模型

note 本文提出了一种名为 OneRec 的统一生成式推荐框架&#xff0c;旨在替代传统的多阶段排序策略&#xff0c;通过一个端到端的生成模型直接生成推荐结果。OneRec 的主要贡献包括&#xff1a; 编码器-解码器结构&#xff1a;采用稀疏混合专家&#xff08;MoE&#xff09;架构…

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、列表 列表分为&#xff1a; 无序列表有序列表定义列表&#xff1a;一个标题下有多个小分类 1.1 无序列表 ul嵌套li&#xff0c;ul是无序列表&#xff0c;li是列表…

​​​​​​​大语言模型安全风险分析及相关解决方案

大语言模型的安全风险可以从多个维度进行分类。 从输入输出的角度来看,存在提示注入、不安全输出处理、恶意内容生成和幻觉错误等风险; 从数据层面来看,训练数据中毒、敏感信息泄露和模型反演攻击是主要威胁; 模型自身则面临拒绝服务和盗窃的风险; 供应链和插件的不安全引…

windows平台的ffmpeg编译使用

windows平台的ffmpeg编译使用 一、现状 本人使用libgdx开发galGame,发现扩展包gdx-video不支持mp4,不能忍,正好看到官网有支持自定义编译的文档,所以操作一下,自定义编译。本文重点在于操作windows平台,linux平台太简单了。 整个过程包括如下几个步骤。 二、代码下载…

FFMPEG录制远程监控摄像头MP4

手绘效果图 上图是录制功能的HTML前端页面&#xff0c;录制功能和解码视频放在一起。录制功能关键是录制(开始录制按钮)、停止录像按钮。当点击“录制”的时候则会开始录制MP4文件, 当点击停止的时候就会停止录制MP4。经过录制后&#xff0c;则会生成MP4,并放到我的RV1126的/tm…

centos操作系统上传和下载百度网盘内容

探序基因 整理 进入百度网盘官网百度网盘 客户端下载 下载linux的rpm格式的安装包 在linux命令行中输入&#xff1a;rpm -ivh baidunetdisk_4.17.7_x86_64.rpm 出现报错&#xff1a; 错误&#xff1a;依赖检测失败&#xff1a; libXScrnSaver 被 baidunetdisk-4.17.7-1.x8…

Rubick:基于 Electron 的开源插件化桌面效率工具箱

Rubick 是一款基于 Electron 构建的开源桌面工具箱&#xff0c;专为追求高效办公和个性化体验的用户设计。它通过自由集成丰富的插件&#xff0c;让用户能够根据自己的需求打造极致的桌面端效率工具。 软件命名由来Rubick 的名字来源于《DOTA2》中的英雄 Rubick&#xff08;拉…

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…