C++ 条件变量:wait、wait_for、wait_until

embedded/2024/9/20 3:19:03/ 标签: c++, 开发语言

前言

在C++中,条件变量(std::condition_variable)是用来在多个线程之间同步执行流的一种机制。它们通常与互斥锁(如std::mutex)一起使用,以在特定条件满足时唤醒一个或多个线程。条件变量有三种使线程阻塞并等待唤醒的方法,分别是waitwait_forwait_until三种方式,三种方式有不同的特点;

内容

Wait

  • 功能wait 函数使当前线程阻塞,直到另一个线程调用 notify_onenotify_all
  • 参数:它需要两个参数:一个 std::unique_lock<std::mutex>(或类似的锁类型),它应该在调用 wait 之前由调用线程锁定,并且在 wait 等待期间由 wait 自动解锁;以及一个函数或可调用对象(通常是一个lambda表达式或函数指针),用于检查条件是否满足。
  • 特点wait 会无限期地等待,直到条件满足。它会在每次从 wait 返回时重新获取互斥锁。
#include <mutex>
#include <condition_variable>
#include <thread> std::mutex mtx; 
std::condition_variable cv; 
bool ready = false; void print_id(int id) 
{ std::unique_lock<std::mutex> lck(mtx); while (!ready) {cv.wait(lck); // 等待ready变为true }// 当ready为true时,继续执行... std::cout << "Thread " << id << '\n'; 
} void go() 
{ std::unique_lock<std::mutex> lck(mtx); ready = true; cv.notify_all(); // 唤醒所有等待的线程 
} int main() 
{ std::thread threads[10]; // 创建10个线程 for (int i=0; i<10; ++i) {threads[i] = std::thread(print_id, i);} std::cout << "10 threads ready to race...\n";go(); // 唤醒所有线程 // 等待所有线程完成 for (auto& th : threads) {th.join(); return 0; }
}

Wait_for

  • 功能wait_for 函数使当前线程阻塞一段指定的时间或直到另一个线程调用 notify_onenotify_all,以先发生者为准。
  • 参数:与 wait 类似,它需要一个 std::unique_lock<std::mutex> 和一个函数或可调用对象来检查条件。此外,它还需要一个表示等待时间的 std::chrono::duration 类型的参数。
  • 特点wait_for 提供了等待时间的上限。如果在指定的时间内条件没有变为真,则 wait_for 会返回,即使 notify_onenotify_all 还没有被调用。
#include <iostream> 
#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <chrono> 
std::mutex mtx; 
std::condition_variable cv; 
bool ready = false; 
void print_id(int id) 
{ std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "Thread " << id << '\n'; 
} 
void print_ready() 
{ std::unique_lock<std::mutex> lck(mtx); while (!ready) { // 循环直到条件满足 cv.wait_for(lck, std::chrono::seconds(2), []{ return ready; }); // 等待直到ready为true或超时 } std::cout << "Ready now\n"; 
} void go()
{ std::unique_lock<std::mutex> lck(mtx); ready = true; cv.notify_one(); // 唤醒一个等待的线程 
} int main() 
{ std::thread threads[10]; // 启动10个线程,它们将等待ready标志 for (int i = 0; i < 10; ++i) threads[i] = std::thread(print_ready); std::cout << "10 threads ready to race...\n";std::thread producer(go); // 等待所有线程完成 for (auto& th : threads) th.join(); producer.join(); return 0;
}

Wait_until

  • 功能wait_until 函数使当前线程阻塞直到指定的时间点或直到另一个线程调用 notify_onenotify_all,以先发生者为准。
  • 参数:与 waitwait_for 类似,它需要一个 std::unique_lock<std::mutex> 和一个函数或可调用对象来检查条件。此外,它还需要一个表示未来某个时间点的 std::chrono::time_point 类型的参数。
  • 特点wait_until 允许指定一个绝对的时间点作为等待的结束条件。如果在指定的时间点之前条件没有变为真,则 wait_until 会返回,即使 notify_onenotify_all 还没有被调用。
#include <iostream>  
#include <thread>  
#include <mutex>  
#include <condition_variable>  
#include <chrono>  
#include <system_clock>  std::mutex mtx;  
std::condition_variable cv;  
bool ready = false;  void print_id(int id, const std::string& threadName) {  // 模拟一些工作  std::this_thread::sleep_for(std::chrono::seconds(1));  std::cout << threadName << " " << id << std::endl;  
}  void wait_for_ready(int id, const std::string& threadName) {  std::unique_lock<std::mutex> lck(mtx);  auto future_time = std::chrono::system_clock::now() + std::chrono::seconds(5); // 等待最多5秒  while (!ready) {  if (cv.wait_until(lck, future_time) == std::cv_status::timeout) {  std::cout << threadName << " " << id << " Timeout! Exiting.\n";  return;  }  }  // 如果ready为true,则继续执行  std::cout << threadName << " " << id << " Ready now.\n";  // 可以在这里处理数据...  
}  void go() {  std::this_thread::sleep_for(std::chrono::seconds(3)); // 生产者准备数据需要一些时间  {  std::unique_lock<std::mutex> lck(mtx);  ready = true;  cv.notify_all(); // 唤醒所有等待的线程  }  // 生产者可以继续执行其他任务...  
}  int main() {  std::thread threads[10];  // 启动10个消费者线程  for (int i = 0; i < 10; ++i) {  threads[i] = std::thread(wait_for_ready, i, "Consumer " + std::to_string(i+1));  }  std::thread producer(go);  // 等待所有消费者线程完成  for (auto& th : threads) {  th.join();  }  producer.join();  return 0;  
}

总结

  • wait:无限期等待直到条件满足。
  • wait_for:等待直到条件满足或指定的时间过去。
  • wait_until:等待直到条件满足或指定的时间点到达。

http://www.ppmy.cn/embedded/114012.html

相关文章

机器人自主导航从零开始第四步———Rviz、Gazebo、Meshlab的安装

本文参考资料&#xff1a; rviz - ROS 维基 Gazebo : Tutorial : Ubuntu (gazebosim.org) 零. 什么是Rviz和Gazebo&#xff1a; Rviz是一个三维可视化工具&#xff0c;它利用已有的数据将数据可视化&#xff0c;并提供了可以显示图像、模型、表格、路径等信息的插件&#x…

JDeps 使用指南

JDeps 使用指南 jdeps 是一个 Java 类依赖分析工具&#xff0c;允许开发者深入分析 Java 应用程序的依赖情况&#xff0c;包括类、包、模块和 JDK 内部 API 的使用。下面详细介绍 jdeps 的常用命令和输出解释。 1. 基本依赖分析 jdeps path/to/yourapp.jar这个命令会分析指定…

34.贪心算法1

0.贪心算法 1.柠檬水找零&#xff08;easy&#xff09; . - 力扣&#xff08;LeetCode&#xff09; 题目解析 算法原理 代码 class Solution {public boolean lemonadeChange(int[] bills) {int five 0, ten 0;for (int x : bills) {if (x 5) // 5 元&#xff1a;直接收下…

基于vue框架的宠物店管理系统的设计与实现4czn0(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,商品分类,服务类型,商品信息,商品订单,宠物服务,服务预约,服务评价,商品咨询 开题报告内容 基于Vue框架的宠物店管理系统的设计与实现开题报告 一、引言 随着宠物行业的蓬勃发展&#xff0c;宠物店作为宠物产品与服务的重要提供…

【代码】使用c#实现串口通信的基础模板

一、分享代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;using System.IO.Ports; using…

在 Red Hat 上安装 SQL Server 2022 并创建数据库

适用于&#xff1a; SQL Server - Linux 本快速入门介绍如何在 Red Hat Enterprise Linux (RHEL) 8.x 或 9.x 上安装 SQL Server 2022 (16.x)。然后可以使用 sqlcmd 进行连接&#xff0c;创建第一个数据库并运行查询。 注意&#xff1a;本教程需要用户输入和 Internet 连接。 …

高级java每日一道面试题-2024年9月16日-框架篇-Spring MVC和Struts的区别是什么?

如果有遗漏,评论区告诉我进行补充 面试官: 如何处理事务中的性能问题&#xff1f; 我回答: 在Java高级面试中&#xff0c;Spring MVC和Struts是两个常被提及的MVC框架&#xff0c;它们各自具有独特的特点和优势。以下是对这两个框架的详细比较&#xff1a; 架构设计方面 Sp…

UDS 诊断 - RequestFileTransfer(请求文件传输)(0x38)服务

UDS 诊断服务系列文章目录 诊断和通信管理功能单元 UDS 诊断 - DiagnosticSessionControl&#xff08;诊断会话控制&#xff09;&#xff08;0x10&#xff09;服务 UDS 诊断 - ECUReset&#xff08;ECU重置&#xff09;&#xff08;0x11&#xff09;服务 UDS 诊断 - SecurityA…

Linux命令---查看端口是否被占用

简介 在linux系统使用命令查找指定端口是否被占用。 命令 lsof -i:6379

【机器学习】参数学习的基本概念以及贝叶斯网络的参数学习和马尔可夫随机场的参数学习

引言 概率图模型参数学习是模型训练的关键环节&#xff0c;涉及贝叶斯网络和马尔可夫随机场的参数估计和结构确定 文章目录 引言一、参数学习任务1.1 定义1.2 常见参数学习任务类型1.2.1 监督学习中的参数学习1.2.2 无监督学习中的参数学习1.2.3 半监督学习中的参数学习1.2.4 强…

Apache subversion 编译流程

目录 1. 概述2. 依赖库简介2.1 Expat2.2 Apache apr2.3 Apache apr-iconv2.4 Apache apr-util2.5 Zlib2.6 OpenSSL2.7 Sqlite2.8 Apache Serf2.9 Apache subversion3. 编译3.1 Expat编译3.1.1 源码信息3.1.2 CMake-GUI3.1.3 编译步骤3.2 APR编译3.2.1 源码信息3.2.2 编译步骤3.…

解决使用nvm ls命令没有出现*的问题

一、引言 在输命令的时候不知道手误写了什么导致node命令用不了&#xff0c;查看环境变量配的nvm对应的路径没问题&#xff0c;试过网上说的修改文件夹名字但是没有用&#xff01;&#xff01; 输入 nvm ls 显示已下载的node版本&#xff0c;发现前面没有* 输入nvm use 使用其中…

基于Java+Mysql实现(web)大型企业管理系统

技术报告 第一章 系统概述 包括用户管理、权限管理、软件项目管理、软件模块管理、测试用例管理、测试任务分配、bug管理等功能。实现公司不同部门间团队协作&#xff0c;管理人员也能够更加有效的把控系统开发的进度。 本实验综合应用JavaWeb编程中的Servlet&#xff0c;JS…

golang 字符串浅析

go的字符串是只读的 测试源代码 package mainimport ("fmt""unsafe" )func swap(x, y string) (string, string) {return y, x }func print_string(obj *string, msg string) {string_ptr : (*[2]uintptr)(unsafe.Pointer(obj))first_obj_addr : string_…

大模型教程:使用 Milvus、vLLM 和 Llama 3.1 搭建 RAG 应用

vLLM 是一个简单易用的 LLM 推理服务库。加州大学伯克利分校于 2024 年 7 月将 vLLM 作为孵化项目正式捐赠给 LF AI & Data Foundation 基金会。欢迎 vLLM 加入 LF AI & Data 大家庭&#xff01;&#x1f389; 在主流的 AI 应用架构中&#xff0c;大语言模型&#xff…

HarmonyOS Next鸿蒙扫一扫功能实现

直接使用的是华为官方提供的api&#xff0c;封装成一个工具类方便调用。 import { common } from kit.AbilityKit; import { scanBarcode, scanCore } from kit.ScanKit;export namespace ScanUtil {export async function startScan(context: common.Context) : Promise<s…

python学习笔记目录

基于windows下docker安装HDDM-CSDN博客 在python中安装HDDM-CSDN博客&#xff08;这个办法没安装成功&#xff09;

前端工程师职业发展路线图

在前端开发领域&#xff0c;从一个新手成长为一名资深工程师需要经过一系列的学习和实践。以下是一份详细的前端工程师职业发展路线图&#xff0c;包括了从基础到高级的各个阶段。 入门阶段 1. 学习基础技术 HTML/HTML5&#xff1a;掌握网页结构和语义化标签的使用。CSS/CSS…

Luban策划开源工具

一、Luban游戏配置解决方案&#xff0c;是一个强大、易用、优雅、稳定的游戏配置解决方案。它设计目标为满足从小型到超大型游戏项目的简单到复杂的游戏配置工作流需求。luban标准化了游戏配置开发工作流&#xff0c;可以极大提升策划和程序的工作效率。 二、核心特性&#xf…

C# UDP与TCP点发【速发速断】模式

1、UDP 客户端 //由于收发都在本机&#xff0c;所以只用一个IP地址 IPAddress addr IPAddress.Parse("127.0.0.1"); var ptLocal new IPEndPoint(addr&#xff0c;9001);//本机节点&#xff0c;用于发送var ptDst new IPEndPoint(addr&#xff0c;9002);//目标节点…