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

news2025/2/21 17:55:45

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。

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

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

相关文章

【Unity动画】导入动画资源到项目中,Animator播放角色动画片段,角色会跟随着动画播放移动。

导入动画资源到项目中&#xff0c;Animator播放角色动画片段,角色会跟随着动画播放移动&#xff0c;但我只想要角色在原地播放动画。比如&#xff1a;播放一个角色Run动画&#xff0c;希望角色在原地奔跑&#xff0c;而不是产生了移动距离。 问题排查&#xff1a; 1.是否勾选…

图解循环神经网络(RNN)

目录 1.循环神经网络介绍 2.网络结构 3.结构分类 4.模型工作原理 5.模型工作示例 6.总结 1.循环神经网络介绍 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;是一种专门用于处理序列数据的神经网络结构。与传统的神经网络不同&#xff0c…

【数据结构】(9) 优先级队列(堆)

一、优先级队列 优先级队列不同于队列&#xff0c;队列是先进先出&#xff0c;优先级队列是优先级最高的先出。一般有两种操作&#xff1a;返回最高优先级对象&#xff0c;添加一个新对象。 二、堆 2.1、什么是堆 堆也是一种数据结构&#xff0c;是一棵完全二叉树&#xff0c…

4、IP查找工具-Angry IP Scanner

在前序文章中&#xff0c;提到了多种IP查找方法&#xff0c;可能回存在不同场景需要使用不同的查找命令&#xff0c;有些不容易记忆&#xff0c;本文将介绍一个比较优秀的IP查找工具&#xff0c;可以应用在连接树莓派或查找IP的其他场景中。供大家参考。 Angry IP Scanner下载…

【Linux】命令操作、打jar包、项目部署

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;Xshell下载 1&#xff1a;镜像设置 二&#xff1a;阿里云设置镜像Ubuntu 三&#xf…

瑞萨RA-T系列芯片ADCGPT功能模块的配合使用

在马达或电源工程中&#xff0c;往往需要采集多路AD信号&#xff0c;且这些信号的优先级和采样时机不相同。本篇介绍在使用RA-T系列芯片建立马达或电源工程时&#xff0c;如何根据需求来设置主要功能模块ADC&GPT&#xff0c;包括采样通道打包和分组&#xff0c;GPT触发启动…

Unity Shader学习6:多盏平行光+点光源 ( 逐像素 ) 前向渲染 (Built-In)

0 、分析 在前向渲染中&#xff0c;对于逐像素光源来说&#xff0c;①ForwardBase中只计算一个平行光&#xff0c;其他的光都是在FowardAdd中计算的&#xff0c;所以为了能够渲染出其他的光照&#xff0c;需要在第二个Pass中再来一遍光照计算。 而有所区别的操作是&#xff0…

tailwindcss学习01

系列教程 01 入门 02 vue中接入 入门 # 注意使用cmd不要powershell npm init -y # 如果没有npx则安装 npm install -g npx npm install -D tailwindcss3.4.17 --registry http://registry.npm.taobao.org npx tailwindcss init修改tailwind.config.js /** type {import(tai…

DIN:引入注意力机制的深度学习推荐系统,

实验和完整代码 完整代码实现和jupyter运行&#xff1a;https://github.com/Myolive-Lin/RecSys--deep-learning-recommendation-system/tree/main 引言 在电商与广告推荐场景中&#xff0c;用户兴趣的多样性和动态变化是核心挑战。传统推荐模型&#xff08;如Embedding &…

【前端】如何安装配置WebStorm软件?

文章目录 前言一、前端开发工具WebStorm和VS Code对比二、官网下载三、安装1、开始安装2、选择安装路径3、安装选项4、选择开始菜单文件夹5、安装成功 四、启动WebStorm五、登录授权六、开始使用 前言 WebStorm 是一款由 JetBrains 公司开发的专业集成开发环境&#xff08;IDE…

【Golang学习之旅】Go 语言微服务架构实践(gRPC、Kafka、Docker、K8s)

文章目录 1. 前言&#xff1a;为什么选择Go语言构建微服务架构1.1 微服务架构的兴趣与挑战1.2 为什么选择Go语言构建微服务架构 2. Go语言简介2.1 Go 语言的特点与应用2.2 Go 语言的生态系统 3. 微服务架构中的 gRPC 实践3.1 什么是 gRPC&#xff1f;3.2 gRPC 在 Go 语言中的实…

Spring核心思想之—AOP(面向切面编程)

目录 一 .AOP概述 二. Spring AOP 使用 2.1 引入AOP依赖 2.2 编写AOP程序 三. Spring AOP详情 3.1 切点(Pointcut) 3.2 连接点(Join Point&#xff09; 3.3通知&#xff08;Advice&#xff09; 3.4切面(Aspect) 3.5通知 3.6 PointCut &#xff08;公共切点&#xff09;…

关于使用雪花算法生成唯一ID,返回给前端ID不一致的问题

问题 在某个项目中,使用雪花算法生成的唯一ID,从数据库查询到数据后返回给前端,但是前端接受到的数据ID和数据库原先生成的不一致 但是前端展示的数据: 原因 原因是后端使用Long类型来存储雪花算法生成的ID,但是这个数值已经超过前端数值类型的范围,导致前端在存储这个数值…

axios post请求 接收sse[eventsource]数据的

axios 接收sse数据的 axios 接收sse数据的 EventSource什么 基于 HTTP 协议实现&#xff0c;通过与服务器建立一个持续连接&#xff0c;实现了服务器向客户端推送事件数据的功能。在客户端&#xff0c;EventSource 对象通过一个 URL 发起与服务器的连接。连接成功后&#xff0…

大语言模型常用微调与基于SFT微调DeepSeek R1指南

概述 大型语言模型&#xff08;LLM&#xff0c;Large Language Model&#xff09;的微调&#xff08;Fine-tuning&#xff09;是指在一个预训练模型的基础上&#xff0c;使用特定领域或任务的数据对模型进行进一步训练&#xff0c;以使其在该领域或任务上表现更好。微调是迁移…

聚焦地灾防治,助力城市地质安全风险防控

城市是人类社会发展的重要载体&#xff0c;承载着经济繁荣、文化交流和人口聚集等重要功能。然而&#xff0c;由于城市建设过程中地质条件复杂&#xff0c;地质灾害风险隐患存在&#xff0c;城市地质安全等问题日益突出&#xff0c;引起人们的广泛关注。为保障城市发展的安全和…

为什么WP建站更适合于谷歌SEO优化?

在当今数字时代&#xff0c;建立一个网站似乎变得容易&#xff0c;但要构建一个真正能够带来流量和订单的网站却并非易事。特别是在谷歌SEO优化方面&#xff0c;不同的建站程序在SEO支持方面的效果差异显著。对于希望提升搜索引擎表现的用户来说&#xff0c;WordPress无疑是最佳…

用deepseek学大模型08-长短时记忆网络 (LSTM)

deepseek.com 从入门到精通长短时记忆网络(LSTM),着重介绍的目标函数&#xff0c;损失函数&#xff0c;梯度下降 标量和矩阵形式的数学推导&#xff0c;pytorch真实能跑的代码案例以及模型,数据&#xff0c; 模型应用场景和优缺点&#xff0c;及如何改进解决及改进方法数据推导…

(蓝桥杯——10. 小郑做志愿者)洛斯里克城志愿者问题详解

题目背景 小郑是一名大学生,她决定通过做志愿者来增加自己的综合分。她的任务是帮助游客解决交通困难的问题。洛斯里克城是一个六朝古都,拥有 N 个区域和古老的地铁系统。地铁线路覆盖了树形结构上的某些路径,游客会询问两个区域是否可以通过某条地铁线路直达,以及有多少条…

小胡说技书博客分类(部分目录):服务治理、数据治理与安全治理对比表格

文章目录 一、对比表格二、目录2.1 服务2.2 数据2.3 安全 一、对比表格 下表从多个维度对服务治理、数据治理和安全治理进行详细对比&#xff0c;为读者提供一个直观而全面的参考框架。 维度服务治理数据治理安全治理定义对软件开发全流程、应用交付及API和接口管理进行规范化…