当前位置: 首页 > 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…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...