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

C++中std::condition_variable_any、std::lock_guard 和 std::unique_

1、背景

在 C++ 多线程编程中,同步 和 互斥 是至关重要的概念。C++ 标准库提供了多种同步机制,其中 std::condition_variable_any、std::lock_guard 和 std::unique_lock 是经常被用到的工具。本文将详细介绍这三者的用途、区别、适用场景,并通过示例展示如何正确使用它们。

2、std::lock_guard

std::lock_guard 是 C++ 提供的一个 RAII(资源获取即初始化)风格的互斥锁管理器,用于在作用域内 自动管理 std::mutex 的加锁和解锁,确保不会发生死锁或者忘记解锁的问题,它是一种轻量级自动管理锁。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void print_message(const std::string& msg) {
    std::lock_guard<std::mutex> lock(mtx); // 自动加锁,作用域结束时自动解锁
    std::cout << msg << std::endl;
}

int main() {
    std::thread t1(print_message, "Hello from thread 1");
    std::thread t2(print_message, "Hello from thread 2");

    t1.join();
    t2.join();

    return 0;
}

该锁主要适用于短生命周期的互斥操作。

3、std::unique_lock

与std::lock_guard相比,std::unique_lock支持更加灵活的互斥锁管理。主要有以下几个功能:

  • 支持 defer_lock(延迟加锁),允许在稍后手动调用 lock();
  • 支持手动 unlock(),适用于更复杂的同步场景。
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void worker() {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 不自动加锁
    std::cout << "Before locking...\n";

    lock.lock(); // 需要时手动加锁
    std::cout << "Worker thread acquired lock\n";

    lock.unlock(); // 需要时手动解锁
    std::cout << "Worker thread released lock\n";
}

int main() {
    std::thread t(worker);
    t.join();
    return 0;
}

  • 支持 try_lock,尝试加锁但不阻塞;
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

std::mutex mtx;

void worker() {
    std::unique_lock<std::mutex> lock(mtx, std::try_to_lock); // 尝试加锁

    if (lock.owns_lock()) { // 判断是否成功加锁
        std::cout << "Worker acquired lock\n";
    } else {
        std::cout << "Worker failed to acquire lock\n";
    }
}

int main() {
    std::unique_lock<std::mutex> main_lock(mtx); // 先加锁,让 worker 线程无法获取锁

    std::thread t(worker);
    std::this_thread::sleep_for(std::chrono::milliseconds(50)); // 模拟主线程持锁一段时间

    main_lock.unlock(); // 释放锁

    t.join();
    return 0;
}

  • 支持 timed_lock,尝试在一定时间内加锁;
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

std::mutex mtx;

void worker() {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟加锁

    if (lock.try_lock_for(std::chrono::seconds(2))) { // 等待最多2秒尝试加锁
        std::cout << "Worker acquired lock\n";
    } else {
        std::cout << "Worker failed to acquire lock within timeout\n";
    }
}

int main() {
    std::unique_lock<std::mutex> main_lock(mtx); // 先加锁

    std::thread t(worker);

    std::this_thread::sleep_for(std::chrono::seconds(3)); // 持锁超过 worker 的等待时间
    main_lock.unlock(); // 释放锁

    t.join();
    return 0;
}

4、std::condition_variable_any

std::condition_variable 只能和 std::unique_lockstd::mutex 结合使用,但是 std::condition_variable_any 可以适配 std::shared_mutex,从而实现 读写者模型。

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <condition_variable>

std::shared_mutex rw_mutex;
std::condition_variable_any cv;
bool data_ready = false;
int data = 0;

// 读线程(多个线程可以共享读取)
void reader(int id) {
    std::shared_lock<std::shared_mutex> lock(rw_mutex);
    cv.wait(lock, [] { return data_ready; }); // 等待数据就绪
    std::cout << "Reader " << id << " read data: " << data << std::endl;
}

// 写线程(独占写入)
void writer() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    {
        std::unique_lock<std::shared_mutex> lock(rw_mutex);
        data = 42;
        data_ready = true;
    }

    cv.notify_all(); // 唤醒所有等待的读线程
}

int main() {
    std::thread readers[3] = {
        std::thread(reader, 1),
        std::thread(reader, 2),
        std::thread(reader, 3)
    };

    std::thread writer_thread(writer);

    for (auto& r : readers) r.join();
    writer_thread.join();

    return 0;
}
  • std::condition_variable_any 与 std::condition_variable 的区别
特性std::condition_variablestd::condition_variable_any
兼容锁类型只能配合std::unique_lock<std::mutx>兼容所有符合Lockable规范的锁如std::unique_lock<std::shared_mutex>
适用范围只能用于std::mutex适用于std::mutex、std::shared_mutex等
适用场景经典生产者-消费者模型适用于读写锁等更多场景
线程等待允许多个线程等待同一条件允许多个线程等待同一条件

5、结论

  • 如果你只需要在作用域内加锁并确保自动释放,使用 std::lock_guard。
  • 如果你需要更灵活的锁管理,如手动解锁或尝试加锁,使用 std::unique_lock。
  • 如果你需要线程间的条件同步,使用 std::condition_variable_any或者std::condition_variable,再根据std::mutex和std::shared_mutex哪个可以满足需求决定使用那一个条件变量,std::condition_variable_any比std::condition_variable功能更加强大,因此它的性能开销应该比std::condition_variable更大,因此如果std::condition_variable可以满足需要,最好使用std::condition_variable,否则使用std::condition_variable_any。

相关文章:

  • UE5控件组件显示UMG文本不正常
  • 1、AI量化学习资料 - 用DEEPSEEK玩转PTrade策略开发.zip\AI量化学习资料 - 1、PTrade策略开发提示词(参考模板).md
  • SpringBoot速成(14)文件上传P23-P26
  • 【JAVA实战】JAVA实现Excel模板下载并填充模板下拉选项数据
  • 【C++】36.C++IO流
  • 级联选择器多选动态加载
  • 洛谷P11042 [蓝桥杯 2024 省 Java B] 类斐波那契循环数
  • Linux系统配置阿里云yum源,安装docker
  • Step-Video-T2V:阶跃星辰发布最强开源视频生成模型(论文详解)
  • 《深度学习》——ResNet网络
  • 单纯禁用Cookie能否保证隐私安全?
  • 跳表的C语言实现
  • MySQL5.7 创建用户并授予超管权限脚本
  • uni-app发起网络请求的三种方式
  • 探讨如何加快 C# 双循环的速度效率
  • 【route】route add命令详解
  • 记一次一波三折的众测SRC经历
  • DevOps自动化部署详解:从理念到实践
  • DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方DeepSeek接入)
  • 从零搭建微服务项目Base(第5章——SpringBoot项目LogBack日志配置+Feign使用)
  • 新疆多地市民拍到不明飞行物:几秒内加速消失,气象部门回应
  • 解锁儿时愿望!潘展乐战胜孙杨,全国冠军赛男子400自夺冠
  • 101岁陕西省军区原司令员冀廷璧逝世,曾参加百团大战
  • 夜读丨什么样的前程值得把春天错过
  • 戛纳打破“疑罪从无”惯例,一法国男演员被拒之门外
  • 收到延期付款利息,该缴纳增值税吗?