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

《多线程基础之条件变量》

【条件变量导读】条件变量是多线程中比较灵活而且容易出错的线程同步手段,比如:虚假唤醒、为啥条件变量要和互斥锁结合使用?windows和linux双平台下,初始化、等待条件变量的api一样吗?

本文将分别为您介绍条件变量在windows和linux平台下的用法和注意事项,好!直接进入主题。
条件变量的使用场景可以用如下流程图进行阐述。
在这里插入图片描述
我们需反复判断一个多线程共享条件是否满足,一直到该条件满足为止(由于该条件被多个线程操作)。因此每次判断前进行加锁操作,判断完毕后解锁。但上述逻辑存在严重的效率问题,假设我们解锁离开临界区后,其他线程修改了条件,导致条件满足了;此时程序仍然需要睡眠 n 秒后才能得到反馈。因此我们需要这样一种机制:

 某个线程 A 在条件不满足的情况下,主动让出互斥锁,让其他线程去争夺这把锁,当前线程A在此处等待,等待条件的满足;一旦条件满足,其他线程释放锁,并通知条件满足,线程A就可以被立刻唤醒并能获取到互斥锁对象。

1、Windows下条件变量的用法

具体条件变量的定义和api,我就不介绍了,大家参考如下示例程序,就能很轻松地掌握条件变量地初始化,本文地重点是介绍条件变量地用法及注意事项。

#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <list>class ThreadTask
{public:ThreadTask(int taskId){m_taskId = taskId;}void doTask(){std::cout << " threadId: " << std::this_thread::get_id() << " do Task, taskId: " << m_taskId << std::endl;}private:int m_taskId;
};/定义全局互斥锁对象
std::mutex myMutex;
//定义全局的windows条件变量
std::condition_variable myCv;
/全局任务队列
std::list<ThreadTask*> taskList;void* consumeThread()
{while (true){/判全局条件(公共队列taskList是否为空)前,先加锁std::unique_lock<std::mutex> lk(myMutex);while (taskList.empty()){ /*如果条件不满足,那继续等待条件变量满足条件同时立刻让出刚占有的互斥锁对象,让其他线程去争抢*/myCv.wait(lk); }		//假设条件满足了,当前线程将从myCv.wait(lk)返回,//并立刻获取互斥锁对象操作公共的全局队列ThreadTask* pTask = taskList.front();//头部弹任务taskList.pop_front();if (!pTask)continue;pTask->doTask();delete pTask;pTask = nullptr;}return nullptr;
}void* produceThread()
{int taskId = 0;while (true){ThreadTask* pTask = nullptr;{std::lock_guard<std::mutex> lk(myMutex);taskId++;pTask = new ThreadTask(taskId);taskList.push_back(pTask);std::cout << "thread: " << std::this_thread::get_id() << " produce a Task, taskId:   " << taskId << std::endl;}/*生产完任务,通知消费线程consumeThread条件满足释放锁资源myMutex*/myCv.notify_one();std::this_thread::sleep_for(std::chrono::seconds(1));}return nullptr;
}int main()
{std::thread consumeThread1(consumeThread);std::thread consumeThread2(consumeThread);std::thread consumeThread3(consumeThread);std::thread produceThread(produceThread);if (produceThread.joinable())produceThread.join();if (consumeThread1.joinable())consumeThread1.join();if (consumeThread2.joinable())consumeThread2.join();if (consumeThread3.joinable())consumeThread3.join();return 0;
}

程序运行的结果:
在这里插入图片描述
可以看出生产线程生产完任务塞到公共队列中去,通知消费线程去公共队列中取任务,一共四个线程在操作公共队列taskList,并没有出现资源冲突的情况。这便是条件变量使用的妙处!

从上述代码中可以看到,条件变量竟然在等待一把互斥锁。

std::unique_lock<std::mutex> lk(myMutex);
while (taskList.empty())myCv.wait(lk);

为啥条件变量要和互斥锁配合一起使用?我们可以假设下面这段伪码,互斥锁和条件变量分开使用。

lock(myMutex)
while (taskList.empty())
{//释放锁unlock(myMutex);/再等待条件cvcond_wait(&cv);//再加锁lock(myMutex)
}

假设线程当前线程(线程A)执行到第5行代码,释放了锁,此时操作系统把CPU时间片分配给另外一个等待myMutex的线程B,随后线程B释放信号,表明条件cv已经满足,等到线程A争抢到CPU时间片之后,就已经错过了线程B释放的信号了,那么线程B将永远阻塞在cond_wait()接口上。

解锁和等待条件变量必须是原子性的操作,要么都成功,要么都不成功,否则就很难保证线程的同步。

还有虚假唤醒的问题,何为虚假唤醒,就是 myCv.wait(lk)接口突然返回了,但它并不是被其它线程的信号唤醒的,可能是被操作系统某个中断信号给唤醒的,此时并没有相应的任务需要处理,如果继续让线程走下去,就可能会有问题,所以为了防止这种虚假唤醒的现象,我们外部循环去判断公共队列是否为空,如果为空,那就继续等待。这是Linux服务端面试必问的考点,请同学们慎重。

好,介绍完条件变量在windows下的用法,那么接着看下条件变量在linux下的用法。

2、Linux下条件变量的用法

条件变量的用法流程和windows的差不多,主要差异就是创建线程、初始化条件变量、等待条件变量的api接口不一样。
那,直接上代码!

#include <iostream>
#include <pthread.h>
#include <error.h>
#include <list>
#include <unistd.h>
#include <semaphore.h>
using namespace std;class ThreadTask
{public:ThreadTask(int taskId){m_taskId = taskId;}void doTask(){cout << " doTask taskId : " << m_taskId << " thread Id: " << pthread_self() << endl;}private:int m_taskId; 
};pthread_mutex_t myMutex;
pthread_cond_t myCond;
list<ThreadTask*> taskList; void* consumeThread(void* param)
{while(true){pthread_mutex_lock(&myMutex);while(taskList.empty()){pthread_cond_wait(&myCond, &myMutex);  }ThreadTask* pTask = taskList.front();taskList.pop_front();pthread_mutex_unlock(&myMutex);if (pTask == nullptr)continue;pTask->doTask();delete pTask;pTask = nullptr;}return NULL;
}
void* produceThread(void* param)
{int taskID = 0;ThreadTask* pTask = NULL;while (true){pTask = new ThreadTask(taskID);pthread_mutex_lock(&myMutex);taskList.push_back(pTask);std::cout << "produce a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl; pthread_mutex_unlock(&myMutex);//释放信号量,通知消费者线程pthread_cond_signal(&myCond);taskID++;sleep(1);}return NULL;
}int main()
{pthread_mutex_init(&myMutex, NULL);pthread_cond_init(&myCond, NULL);//创建3个消费者线程pthread_t consumerThreadID[5];for (int i = 0; i < 3; ++i){pthread_create(&consumerThreadID[i], NULL, consumeThread, NULL);}//创建一个生产者线程pthread_t producerThreadID;pthread_create(&producerThreadID, NULL, produceThread, NULL);pthread_join(producerThreadID, NULL);for (int i = 0; i < 3; ++i){pthread_join(consumerThreadID[i], NULL);}pthread_cond_destroy(&myCond);pthread_mutex_destroy(&myMutex);   return 0;
}

Linux平台下运行的结果:
在这里插入图片描述

相关文章:

《多线程基础之条件变量》

【条件变量导读】条件变量是多线程中比较灵活而且容易出错的线程同步手段&#xff0c;比如&#xff1a;虚假唤醒、为啥条件变量要和互斥锁结合使用&#xff1f;windows和linux双平台下&#xff0c;初始化、等待条件变量的api一样吗&#xff1f; 本文将分别为您介绍条件变量在w…...

21款炫酷烟花合集

系列专栏 《Python趣味编程》《C/C趣味编程》《HTML趣味编程》《Java趣味编程》 写在前面 Python、C/C、HTML、Java等4种语言实现18款炫酷烟花的代码。 Python Python烟花① 完整代码&#xff1a;Python动漫烟花&#xff08;完整代码&#xff09; ​ Python烟花② 完整…...

智能风控 数据分析 groupby、apply、reset_index组合拳

目录 groupby——分组 本例 apply——对每个分组应用一个函数 等价用法 reset_index——重置索引 使用前​编辑 注意事项 groupby必须配合聚合函数、 关于agglist 一些groupby试验 1. groupby对象之后。sum&#xff08;一个列名&#xff09; 2. groupby对象…...

Python网络自动化运维---用户交互模块

文章目录 目录 文章目录 前言 实验环境准备 一.input函数 代码分段解析 二.getpass模块 前言 在前面的SSH模块章节中&#xff0c;我们都是将提供SSH服务的设备的账户/密码直接写入到python代码中&#xff0c;这样很容易导致账户/密码泄露&#xff0c;而使用Python中的用户交…...

【JVM】调优

目的&#xff1a; 减少minor gc、full gc的次数&#xff0c;也就是减少STW的时间&#xff0c;因为java虚拟机在做后台垃圾收集线程的时候&#xff0c;会停掉其他线程&#xff0c;专门做垃圾收集&#xff0c;这样会影响网站的性能&#xff0c;以及用户的体验。 调优位置&#x…...

软件测试 —— jmeter(2)

软件测试 —— jmeter&#xff08;2&#xff09; HTTP默认请求头&#xff08;元件&#xff09;元件作用域和取样器作用域HTTP Cookie管理器同步定时器jmeter插件梯度压测线程组&#xff08;Stepping Thread Group&#xff09;参数解析总结 Response Times over TimeActive Thre…...

为什么LabVIEW适合软硬件结合的项目?

LabVIEW是一种基于图形化编程的开发平台&#xff0c;广泛应用于软硬件结合的项目中。其强大的硬件接口支持、实时数据采集能力、并行处理能力和直观的用户界面&#xff0c;使得它成为工业控制、仪器仪表、自动化测试等领域中软硬件系统集成的理想选择。LabVIEW的设计哲学强调模…...

【机器学习】自定义数据集 使用tensorflow框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测

一、使用tensorflow框架实现逻辑回归 1. 数据部分&#xff1a; 首先自定义了一个简单的数据集&#xff0c;特征 X 是 100 个随机样本&#xff0c;每个样本一个特征&#xff0c;目标值 y 基于线性关系并添加了噪声。tensorflow框架不需要numpy 数组转换为相应的张量&#xff0…...

.NET Core缓存

目录 缓存的概念 客户端响应缓存 cache-control 服务器端响应缓存 内存缓存&#xff08;In-memory cache&#xff09; 用法 GetOrCreateAsync 缓存过期时间策略 缓存的过期时间 解决方法&#xff1a; 两种过期时间策略&#xff1a; 绝对过期时间 滑动过期时间 两…...

GA-CNN-LSTM-Attention、CNN-LSTM-Attention、GA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比

GA-CNN-LSTM-Attention、CNN-LSTM-Attention、GA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比 目录 GA-CNN-LSTM-Attention、CNN-LSTM-Attention、GA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于GA-CNN-LST…...

git Bash通过SSH key 登录github的详细步骤

1 问题 通过在windows 终端中的通过git登录github 不再是通过密码登录了&#xff0c;需要本地生成一个密钥&#xff0c;配置到gihub中才能使用 2 步骤 &#xff08;1&#xff09;首先配置用户名和邮箱 git config --global user.name "用户名"git config --global…...

《企业应用架构模式》笔记

领域逻辑 表模块和数据集一起工作-> 先查询出一个记录集&#xff0c;再根据数据集生成一个&#xff08;如合同&#xff09;对象&#xff0c;然后调用合同对象的方法。 这看起来很想service查询出一个对象&#xff0c;但调用的是对象的方法&#xff0c;这看起来像是充血模型…...

深入理解 C 语言函数指针的高级用法:(void (*) (void *)) _IO_funlockfile

深入理解 C 语言函数指针的高级用法 函数指针是 C 语言中极具威力的特性&#xff0c;广泛用于实现回调、动态函数调用以及灵活的程序设计。然而&#xff0c;复杂的函数指针声明常常让即使是有经验的开发者也感到困惑。本文将从函数指针的基本概念出发&#xff0c;逐步解析复杂…...

【JavaSE】图书管理系统

前言&#xff1a;为了巩固之前学习的java知识点&#xff0c;我们用之前学习的java知识点&#xff08;方法&#xff0c;数组&#xff0c;类和对象&#xff0c;封装&#xff0c;继承&#xff0c;多态&#xff0c;抽象类&#xff0c;接口&#xff09;来实现一个简单的图书管理系统…...

【C++数论】880. 索引处的解码字符串|2010

本文涉及知识点 数论&#xff1a;质数、最大公约数、菲蜀定理 LeetCode880. 索引处的解码字符串 给定一个编码字符串 s 。请你找出 解码字符串 并将其写入磁带。解码时&#xff0c;从编码字符串中 每次读取一个字符 &#xff0c;并采取以下步骤&#xff1a; 如果所读的字符是…...

C++/stack_queue

目录 1.stack 1.1stack的介绍 1.2stack的使用 练习题&#xff1a; 1.3stack的模拟实现 2.queue的介绍和使用 2.1queue的介绍 2.2queue的使用 2.3queue的模拟实现 3.priority_queue的介绍和使用 3.1priority_queue的介绍 3.2priority_queue的使用 欢迎 1.stack 1.1stack…...

浅谈APP之历史股票通过echarts绘图

浅谈APP之历史股票通过echarts绘图 需求描述 今天我们需要做一个简单的历史股票收盘价格通过echarts进行绘图&#xff0c;效果如下&#xff1a; 业务实现 代码框架 代码框架如下&#xff1a; . 依赖包下载 我们通过网站下载自己需要的涉及的图标&#xff0c;勾选之后进…...

Ubuntu 20.04 x64下 编译安装ffmpeg

试验的ffmpeg版本 4.1.3 本文使用的config命令 ./configure --prefixhost --enable-shared --disable-static --disable-doc --enable-postproc --enable-gpl --enable-swscale --enable-nonfree --enable-libfdk-aac --enable-decoderh264 --enable-libx265 --enable-libx…...

【橘子Kibana】Kibana的分析能力Analytics简易分析

一、kibana是啥&#xff0c;能干嘛 我们经常会用es来实现一些关于检索&#xff0c;关于分析的业务。但是es本身并没有UI,我们只能通过调用api来完成一些能力。而kibana就是他的一个外置UI&#xff0c;你完全可以这么理解。 当我们进入kibana的主页的时候你可以看到这样的布局。…...

【STM32】-TTP223B触摸开关

前言 本文章旨在记录博主STM32的学习经验&#xff0c;我自身也在不断的学习当中&#xff0c;如果文章有写的不对的地方&#xff0c;欢迎各位大佬批评指正。 准备工作 今天这篇文章介绍的是触摸开关这一外围硬件。 ST-link调试器STM32最小系统板单路TTP223B触摸传感器模块LE…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...