当前位置: 首页 > news >正文

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

相关文章:

  • OpenAI与谷歌DeepMind新品同日竞技,谁能引领机器人现实任务新潮流?
  • C#-委托delegate
  • C++设计模式-观察者模式:从基本介绍,内部原理、应用场景、使用方法,常见问题和解决方案进行深度解析
  • 网络视频监控平台在医疗领域的应用
  • 浏览器中输入 URL 到显示主页的完整过程
  • 【后端】【django】Django 自带的用户系统与 RBAC 机制
  • 历次科技泡沫对人工智能发展的启示与规避措施
  • containerd 拉取镜像的工具以及优劣
  • Python----计算机视觉处理(opencv:图片灰度化)
  • go 安装swagger
  • 【论文精读】Deformable DETR:用于端到端目标检测可变形 Transformer
  • go 加载yaml配置文件
  • 3-1 写分享报告
  • 鸿蒙编译框架插件HvigorPlugin接口的用法介绍
  • zuul路由转发功能的核心流程
  • 【docker系】docker安装数据迁移
  • 【含文档+PPT+源码】基于Django框架的乡村绿色农产品交易平台的设计与实现
  • 论文分享:PL-ALF框架实现无人机低纹理环境自主飞行
  • KICK第五课:Mac 系统下安装 Xcode 或 Clang
  • BGP路由聚合
  • 巴基斯坦称对印度发起军事行动
  • 招商蛇口:今年前4个月销售额约498.34亿元
  • 理财经理泄露客户信息案进展:湖南省检受理申诉,证监会交由地方监管局办理
  • 1450亿元!财政部拟发行2025年中央金融机构注资特别国债(二期)
  • 陕西永寿4岁女童被蜜蜂蜇伤致死,当地镇政府介入处理
  • 正荣地产:董事会主席、行政总裁辞任,拟投入更多精力推动境内债重组等工作