C++中的condition_variable:条件变量

理解 C++ 中的条件变量(Condition Variable)

在多线程编程中,我们常常需要一个线程等待某个条件的变化,比如等待数据的生成或某个标志位的设置。如果没有条件变量(condition_variable),线程可能会使用忙等待(不断检查条件是否满足),这会导致 CPU 资源的浪费。条件变量提供了一种高效的等待机制,使线程在等待条件时进入休眠状态,不占用 CPU 资源,当条件满足时被唤醒继续执行。

条件变量的基本概念

条件变量允许一个或多个线程同时阻塞。一般情况下,生产者线程利用支持 std::mutexstd::lock_guardstd::unique_lock 修改共享变量后,并通知条件变量。消费者线程获取同一个 std::mutex(由 std::unique_lock 持有),并调用 std::condition_variablewaitwait_forwait_untilwait 操作会释放互斥量,同时挂起该线程。当条件变量收到通知、超时到期或发生虚假唤醒时,线程被唤醒,互斥量也会被原子地重新获取。如果是虚假唤醒,线程应该检查条件并继续等待,以保证业务的正确性。

注意事项

  1. 使用 unique_lock 而不是 lock_guard
    在等待时,管理 mutex 使用的是 unique_lock 而不是 lock_guard,因为等待时是不持有锁的。wait 函数会调用 mutexunlock 函数,之后再睡眠,直到被唤醒后才持有锁。lock_guard 没有 lock/unlock 接口,所以需要用 unique_lock

  2. 伪唤醒
    在对 wait 函数的调用中,条件变量可能会对提供的条件检查任意多次。这发生在互斥元被锁定的情况下,并且当测试条件返回 true 时就会立即返回。当等待线程重新获取互斥元并检测条件时,如果它并非直接响应另一个线程的通知,这就是所谓的伪唤醒(spurious wake)。伪唤醒的次数和频率根据定义是不确定的。

  3. 通知丢失
    如果发送方在接收方进入等待状态之前发送通知,则通知会丢失。

使用场景

  1. 生产者-消费者问题
    生产者线程生成数据并通知消费者线程处理数据。

  2. 任务队列
    多个线程从一个任务队列中获取任务并处理,当队列为空时,线程进入等待状态,直到有新的任务被添加。

  3. 事件等待
    一个或多个线程等待某个事件的发生,当事件发生时,唤醒等待的线程进行处理。

std::condition_variablestd::condition_variable_any 的区别

  1. 互斥锁类型

    • std::condition_variable 只与 std::unique_lock<std::mutex> 类型的锁配合使用。这意味着它只能与 std::mutex 类型的互斥锁一起使用。
    • std::condition_variable_any 可以与任何符合基本锁(BasicLockable)和锁互换(Lockable)概念的锁对象配合使用。它不仅可以与 std::mutex 配合,还可以与其他类型的锁(例如 std::shared_mutex、用户定义的互斥锁等)一起使用。
  2. 灵活性

    • std::condition_variable 更加专用,提供了一种高效的实现,因为它只支持 std::unique_lock<std::mutex>
    • std::condition_variable_any 更加通用,提供了更大的灵活性,可以与任何符合要求的锁一起使用,因此在某些情况下更为便利。

为什么要有 std::condition_variable_any

std::condition_variable_any 的引入是为了提供更大的灵活性,允许开发者使用不同类型的锁来实现同步需求。在某些情况下,开发者可能需要使用自定义的锁类型或者 std::shared_mutex 这样的共享锁,这时 std::condition_variable 就无法满足需求,而 std::condition_variable_any 则可以适应这些情况。

代码示例

以下是一个使用条件变量的代码示例,演示了如何在 C++ 中使用 std::condition_variable 进行线程同步:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>std::mutex read_mutex_;
std::condition_variable condition_variable_;
bool readFlag_ = false;void do_print_id(int id) {std::unique_lock<std::mutex> uniqueLock(read_mutex_);/*** todo -* 只有当 __pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 __pred 为 true 时才会被解除阻塞* 1. 线程第一次执行到这里时,会执行表达式方法。判断到 ready 为false。则当前线程阻塞在此,同时解锁互斥量,不影响其他线程获取锁* 2. 当线程被唤醒,首先就是不断的尝试重新获取并加锁互斥量,若获取不到锁就卡在这里反复尝试加锁* 3. 若获取到了锁,就执行表达式方法,然后继续往下执行** 函数原型:void condition_variable::wait(unique_lock<mutex>& __lk, _Predicate __pred)*/condition_variable_.wait(uniqueLock, [&] {std::cout << "condition_variable wait.id: " << id << std::endl;return readFlag_;});std::cout << "do_print_id -> thread : " << id << std::endl;
}class QConditionVariable {
public:void task1() {std::thread threads[10];for (int i = 0; i < 10; ++i) {threads[i] = std::thread(do_print_id, i);}for (std::thread &item : threads) {item.detach();}std::this_thread::sleep_for(std::chrono::seconds(2));notify();std::this_thread::sleep_for(std::chrono::seconds(5));std::cout << "task1--end" << std::endl;}void task2() {std::unique_lock<std::mutex> uniqueLock(read_mutex_);std::cv_status cvStatus = condition_variable_.wait_for(uniqueLock, std::chrono::seconds(5));if (cvStatus == std::cv_status::no_timeout) {//表示条件变量等待成功(条件满足或被通知)。} else if (cvStatus == std::cv_status::timeout) {//表示条件变量等待超时。}}void task3() {std::unique_lock<std::mutex> uniqueLock(read_mutex_);/*** wait_for: 等待特定的时间段,直到被通知或时间到期。* 执行到这里时,当前线程将进入等待状态,等待最多1秒钟:*  如果在这1秒钟内,条件变量 condition_variable_ 被其他线程通知(通常通过 notify_one 或 notify_all),wait_for 会立即返回 std::cv_status::no_timeout,并且 while 循环会终止。*  如果这1秒钟内没有收到通知,wait_for 返回 std::cv_status::timeout,循环条件为真,线程继续执行循环体内的代码(这里是空的,没有其他操作),然后再次进入等待。*/while (condition_variable_.wait_for(uniqueLock, std::chrono::seconds(1)) == std::cv_status::timeout) {}}private:void notify() {std::cout << "start----notify" << std::endl;std::unique_lock<std::mutex> uniqueLockNotify(read_mutex_);readFlag_ = true;condition_variable_.notify_all();std::cout << "end----notify" << std::endl;}
};

在这个示例中,我们创建了一个简单的类 QConditionVariable,包含了两个任务 task1task2task1 生成 10 个线程,每个线程都会调用 do_print_id 函数,等待条件变量 condition_variable_ 的通知。task2 则是一个等待操作示例,等待特定时间段或直到被通知。

通过条件变量,我们可以有效地管理多线程程序中的同步和通信,避免忙等待,提高程序的执行效率。

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

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

相关文章

启智畅想火车类集装箱号码识别技术,软硬件解决方案

集装箱号码识别需求&#xff1a; 实时检测车皮号、火车底盘号码、集装箱号码&#xff0c;根据火车类型分为以下三种情况&#xff1a; 1、纯车皮&#xff0c;只检测车皮号&#xff1b; 2、火车拉货箱&#xff08;半车皮&#xff09;&#xff0c;检测车皮号集装箱号码&#xff1b…

如何从0搭建一个Ai智体day01

&#x1f4da;《AI破局行动&#xff5c;AI智能体&#xff08;coze&#xff09;实战手册》&#xff1a; https://d16rg8unadx.feishu.cn/wiki/XQESwHW5HiPFlrkZbkqc0Xp7nEb 说明 这个是授权访问的&#xff0c;想学习加我 微信/ Github:** watchpoints &#x1f4fa;Day1-大圣直播…

玩转HarmonyOS NEXT之常用布局三

轮播&#xff08;Swiper&#xff09; Swiper组件提供滑动轮播显示的能力。Swiper本身是一个容器组件&#xff0c;当设置了多个子组件后&#xff0c;可以对这些子组件进行轮播显示。通常&#xff0c;在一些应用首页显示推荐的内容时&#xff0c;需要用到轮播显示的能力。 针对…

git开发流程

分支介绍 master - 主分支 所有提供给用户使用的正式版本&#xff0c;都在这个主分支上发布 开发者在此分支 不可进行 push 操作 dev - 开发分支 日常开发所使用的分支&#xff0c;开发者完成的阶段性功能模块将首先被合并到此分支 此分支亦是团队内部测试、阶段性工作验证…

Xcode 16 beta3 真机调试找不到 Apple Watch 的尝试解决

很多小伙伴们想用 Xcode 在 Apple Watch 真机上调试运行 App 时却发现&#xff1a;在 Xcode 设备管理器中压根找不到对应的 Apple Watch 设备。 大家是否已将 Apple Watch 和 Mac 都重启一万多遍了&#xff0c;还是束手无策。 Apple Watch not showing in XCodeApple Watch wo…

C++基础语法:STL之容器(1)--容器概述和序列概述

前言 "打牢基础,万事不愁" .C的基础语法的学习 引入 STL是标准模板库,类模板主要是用来做容器的,所以个人理解:标准模板库是"标准容器库".容器是STL的核心 .以<C Prime Plus> 6th Edition(以下称"本书")内容理解容器. 类模板内容回顾 类…

NineData全面支持PostgreSQL可视化表结构设计

“PostgreSQL 是最像 Oracle 的开源关系型数据库“&#xff0c;也正因为如此&#xff0c;很多企业都青睐 PostgreSQL&#xff0c;拿它当成 Oracle 的替代品。所以毫无疑问&#xff0c;目前 PostgreSQL 在企业中非常常见。 对于直接接触 PostgreSQL 的开发人员而言&#xff0c;…

echarts多柱堆叠的X轴顺序

在一些图表场景中&#xff0c;需要显示多柱堆叠的数据&#xff0c;那么X轴上每一段单位区域内会有多根柱子&#xff0c;每一根柱子标识不同的数量项含义&#xff0c;那么怎样控制这几根柱的左右顺序呢&#xff1f; 其实这跟echarts的option里的series由关&#xff0c;开始我以为…

快速排序及归并排序的实现与排序的稳定性

目录 快速排序 一. 快速排序递归的实现方法 1. 左右指针法 步骤思路 为什么要让end先走&#xff1f; 2. 挖坑法 步骤思路 3. 前后指针法 步骤思路 二. 快速排序的时间和空间复杂度 1. 时间复杂度 2. 空间复杂度 三. 快速排序的优化方法 1. 三数取中优化 2. 小区…

实验丰富、原创改进!|多策略改进蜣螂优化算法(MATLAB)

本文内容来源于本人公众号&#xff1a;KAU的云实验台&#xff0c;更新内容&#xff1a;智能优化算法及其改进应用。 本文核心内容&#xff1a; 新颖的多策略改进蜣螂优化算法 对比算法包括&#xff1a;高引用/新发布/经典/其他DBO变体&#xff08;共11种&#xff09; 实验设计…

用c语言写一个贪吃蛇游戏

贪吃蛇游戏通常涉及到终端图形编程和简单的游戏逻辑。以下是一个基本的实现示例&#xff0c;包括贪吃蛇的移动、食物生成、碰撞检测等功能。 1. 贪吃蛇游戏的基本结构 贪吃蛇游戏可以分为以下几个部分&#xff1a; 游戏地图和终端绘制&#xff1a;使用二维数组表示游戏地图&am…

SpringBoot结合ip2region实现博客评论显示IP属地

你好呀&#xff0c;我是小邹。 在现代的Web应用中&#xff0c;特别是博客和论坛类网站&#xff0c;为用户提供地理定位服务&#xff08;如显示用户所在地理位置&#xff09;可以极大地增强用户体验。本文将详细探讨如何使用Java和相关技术栈来实现在博客评论中显示用户的地址信…

Java实验3

实验内容 学生信息管理系统 学生成绩表Student(Sno 字符串&#xff0c;长度9, Sname 字符串&#xff0c;长度10, Class 字符串&#xff0c;长度10, Age 整型, Sex 字符串&#xff0c;长度2) 实现如下功能&#xff1a; A&#xff0e;输入若干个学生的信息到Student表&#x…

初学Python必须知道的14个强大单行代码

引言&#xff1a;Python的魅力与单行代码的重要性 Python以其简洁明了的语法、丰富的内置函数和强大的第三方库深受广大开发者喜爱。尤其对于编程小白来说&#xff0c;学习Python就像打开了一扇通向编程世界的大门。而单行代码&#xff0c;作为Python魅力的一部分&#xff0c;…

【NetTopologySuite类库】合并所有几何的包围盒AABB

流程示意图 示例代码 using GeoAPI.Geometries; using Microsoft.VisualStudio.TestTools.UnitTesting; using NetTopologySuite.Geometries; using NetTopologySuite.IO; using System.Collections.Generic; using System.Linq;namespace Test472 {[TestClass]public class T…

深度解析:电商订单API及其技术实现

随着电子商务的发展&#xff0c;实体企业开拓电商渠道的越来越多&#xff0c;原有的管理系统都需要增加电商业务管理功能&#xff0c;其中&#xff0c;对电商订单的管理是每一个电商商家都需要的功能&#xff0c;所以对于开发者来说&#xff0c;了解电商API是什么是非常重要的&…

第100+16步 ChatGPT学习:R实现Xgboost分类

基于R 4.2.2版本演示 一、写在前面 有不少大佬问做机器学习分类能不能用R语言&#xff0c;不想学Python咯。 答曰&#xff1a;可&#xff01;用GPT或者Kimi转一下就得了呗。 加上最近也没啥内容写了&#xff0c;就帮各位搬运一下吧。 二、R代码实现Xgboost分类 &#xff08…

LeetCode题练习与总结:比较版本号--165

一、题目描述 给你两个 版本号字符串 version1 和 version2 &#xff0c;请你比较它们。版本号由被点 . 分开的修订号组成。修订号的值 是它 转换为整数 并忽略前导零。 比较版本号时&#xff0c;请按 从左到右的顺序 依次比较它们的修订号。如果其中一个版本字符串的修订号较…

C++动态内存的管理

今天来分享C动态内存管理相关知识&#xff0c;闲言勿谈&#xff0c;直接上干货。 1. 动态内存的开辟和销毁(new和delete) (1)前置知识&#xff1a;我们知道c语言有malloc和calloc和realloc三个函数可以进行动态的开辟内存&#xff0c;那么它们有什么区别呢&#xff1f;首先是…

MPS 后端

本文来自&#xff1a; https://pytorch.org/docs/stable/notes/mps.html https://pytorch.ac.cn/docs/stable/notes/mps.html MPS 后端 mps 设备支持 在使用 Metal 编程框架的 MacOS 设备上&#xff0c;进行高性能 GPU 训练。 它引入了新的设备&#xff0c;将机器学习计算图和…