【C++11多线程】线程同步之线程通信:condition_variable

news2024/11/19 15:10:37

文章目录

  • 1.condition_variable
  • 2.notify_one()和notify_all()
  • 3.wait()
    • 3.1 没有第二个参数:while + wait()
    • 3.2 有第二个参数:wait() + lambda

需要注意的是,条件变量condition_variable要和互斥锁mutex搭配起来使用。

1.condition_variable

condition_variable 是一个类,其部分源码如下图所示:

在这里插入图片描述

2.notify_one()和notify_all()

notify_one():通知一个处于等待状态的线程。

notify_all():通知所有处于等待状态的线程。

3.wait()

3.1 没有第二个参数:while + wait()

首先使用 unique_lock 给互斥量加锁。

如果 while 的循环条件是 false,则不进入 while 循环内,那么流程继续往下执行(此时互斥量是锁着的)。

如果 while 的循环条件是 true,则进入 while 循环内,那么 wait() 将解锁互斥量,并进入等待状态,一直等到其它某个线程调用 notify_one()/notify_all() 通知为止。

当收到其它某个线程的通知后,便从等待状态进入阻塞状态,此时 wait() 不断尝试重新获取互斥锁,如果获取不到,那么就阻塞在这里等待获取;如果获取到了,那么 wait() 返回,接着判断 while 的循环条件:

  • 如果 while 的循环条件是 false,则不进入 while 循环内,那么流程继续往下执行(此时互斥量是锁着的)。

  • 如果 while 的循环条件是 true,则进入 while 循环内,那么 wait() 将解锁互斥量,并进入等待状态,一直等到其它某个线程调用 notify_one()/notify_all() 通知为止。

场景:生产者生产一个物品,通知消费者消费一个物品;消费完了,消费者再通知生产者继续生产一个物品。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;

std::mutex mtx; // 定义互斥锁

std::condition_variable cv; // 定义条件变量

class Queue
{
public:
	void put(int val) // 生产一个物品
	{
		unique_lock<std::mutex> unilck(mtx);
		while (!que.empty())
		{
			cv.wait(unilck);
		}
		que.push(val);
		cv.notify_all();
		cout << "put - " << val << endl;
	}

	int get() // 消费一个物品
	{
		unique_lock<std::mutex> lck(mtx);
		while (que.empty())
		{
			cv.wait(lck);
		}
		int val = que.front();
		que.pop();
		cv.notify_all();
		cout << "get - " << val << endl;
		return val;
	}

private:
	queue<int> que; // C++中STL所有的容器都不是线程安全的
};

void producer(Queue* que) // 生产者线程入口函数
{
	for (int i = 1; i <= 100; ++i)
	{
		que->put(i);
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}

void consumer(Queue* que) // 消费者线程入口函数
{
	for (int i = 1; i <= 100; ++i)
	{
		que->get();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}

int main()
{
	Queue que; // 生产者线程和消费者线程共享的队列

	std::thread t1(producer, &que); // 创建了线程,线程入口函数是producer(),生产者线程开始执行
	std::thread t2(consumer, &que); // 创建了线程,线程入口函数是consumer(),消费者线程开始执行

	t1.join();
	t2.join();

	return 0;
}

3.2 有第二个参数:wait() + lambda

首先使用 unique_lock 给互斥量加锁。

如果 lambda 表达式的返回值是 true,那么 wait() 直接返回,流程继续往下执行(此时互斥量是锁着的)。

如果 lambda 表达式的返回值是 false,那么 wait() 将解锁互斥量,并进入等待状态,一直等到其它某个线程调用 notify_one()/notify_all() 通知为止。

当收到其它某个线程的通知后,便从等待状态进入阻塞状态,此时 wait() 不断尝试重新获取互斥锁,如果获取不到,那么就阻塞在这里等待获取;如果获取到了,接着判断这个 lambda 表达式:

  • 如果 lambda 表达式的返回值是 true,那么 wait() 直接返回,流程继续往下执行(此时互斥量是锁着的)。

  • 如果 lambda 表达式的返回值是 false,那么 wait() 将解锁互斥量,并进入等待状态,一直等到其它某个线程调用 notify_one()/notify_all() 通知为止。

场景:生产者生产一个物品,通知消费者消费一个物品;消费完了,消费者再通知生产者继续生产一个物品。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;

std::mutex mtx; // 定义互斥锁

std::condition_variable cv; // 定义条件变量

class Queue
{
public:
	void put(int val) // 生产一个物品
	{
		unique_lock<std::mutex> unilck(mtx);
		cv.wait(unilck, [this] { return que.empty(); });
		que.push(val);
		cv.notify_all();
		cout << "put - " << val << endl;
	}

	int get() // 消费一个物品
	{
		unique_lock<std::mutex> lck(mtx);
		cv.wait(lck, [this] { return !que.empty(); });
		int val = que.front();
		que.pop();
		cv.notify_all();
		cout << "get - " << val << endl;
		return val;
	}

private:
	queue<int> que; // C++中STL所有的容器都不是线程安全的
};

void producer(Queue* que) // 生产者线程入口函数
{
	for (int i = 1; i <= 100; ++i)
	{
		que->put(i);
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}

void consumer(Queue* que) // 消费者线程入口函数
{
	for (int i = 1; i <= 100; ++i)
	{
		que->get();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}

int main()
{
	Queue que; // 生产者线程和消费者线程共享的队列

	std::thread t1(producer, &que); // 创建了线程,线程入口函数是producer(),生产者线程开始执行
	std::thread t2(consumer, &que); // 创建了线程,线程入口函数是consumer(),消费者线程开始执行

	t1.join();
	t2.join();

	return 0;
}

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

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

相关文章

Windows远程连接Redis(Linux)

Windows远程连接Redis&#xff08;Linux&#xff09; 文章目录Windows远程连接Redis&#xff08;Linux&#xff09;1、写在前面2、配置redis.conf3、启动Redis3.1 开启redis服务3.2 启动客户端3.3 Redis命令3.4 查看Redis密码4、关闭Redis5、Java操作Redis1、写在前面 Windows…

安科瑞ARTU100系列模块化远程控制终端单元 开关量输入输出采集模块

安科瑞 王晶淼/刘芳 1.概述 ARTU系列远程终端单元是高性能配电智能化元件&#xff0c;应用于智能配电、工业自动化等领域。ARTU100系列远程终端单元提供开关量输入、开关量输出、模拟量输入、模拟量输出&#xff0c;能够将采集到的信号通过RS485串口、RJ45以太网接口、2G、Lo…

进制+异或

一.调用API String sInteger.toString(十进制&#xff0c;转为0-35进制)&#xff1b; //R进制的字符串转为10进制数 int aInteger.parseInt(s,R); //把R进制的字符串封装成大数类 BigInteger bnew BigInteger(s,R); 二.求R进制下的数位和 2992这个数十进制和为22&#xff0c;…

【计算机视觉】图像分割中FCN、DeepLab、SegNet、U-Net、Mask R-CNN等算法的讲解(图文解释 超详细)

觉得有帮助请点赞关注收藏~~~ 一、FCN分割算法 全卷积神经网络目标分割算法能够端到端的得到每个像素的目标分类结果&#xff0c;与传统的卷积神经网络只能输入固定大小图像和在网络的末端使用几个全连接层得到固定长度的特征向量不同&#xff0c;全卷积神经网络能够接受任意大…

HBase的数据模型和存储原理

HBase的数据模型 HBase中表的逻辑结构 Name Space&#xff08;命名空间&#xff09; 类似于关系型数据库的 DatabBase 概念&#xff0c;每个命名空间下有多个表。HBase有两个自带的命名空间&#xff0c;分别是 hbase 和 default&#xff0c;hbase 中存放的是 HBase 内置的表&a…

向 Excel 和 PowerPoint 添加可编辑地图的指南

本指南规定: 如何将 shapefile 转换为 svg 文件(如果后者不存在) 如何使用 Excel 重命名 svg 文件中的对象以使对象具有正确的地理名称,以及如何将这些添加为 PowerPoint 中的可编辑地图 这样做的目的是为那些没有或不知道该软件的人提供 QGIS 制图的替代方案。通过提供某个…

如何在 Linux 的 shell 里针对特定用户/组来限制某些命令的使用

0-前言 最近&#xff0c;业务侧有个需求&#xff0c;需要禁止特定用户访问linux特定的命令&#xff0c;如禁止用户A使用rm命令。 我们知道&#xff0c;在linux系统中&#xff0c;一切皆文件。 那么&#xff0c;这个问题也可以泛化为&#xff1a; 如何在linux里限制特定用户…

视频理解论文串讲(上)【论文精读】

文章目录1. DeepVedio【CNN 首次 for 视频理解】2. Two-Stream【双流网络】针对上面不同方向&#xff0c;有不同的代表工作LSTM late fusion 方向【Beyond-short-snippets】Early fusion方向【Convolutional fusion】长视频 【TSN temporal segment 分段的思想&#xff0c;good…

5分钟搞定Linux top命令的基本用法

在linux终端中&#xff0c;输入top, 按下Enter&#xff0c;立即进入top界面 如果你操作正确&#xff0c;应该会看到了下面这样的界面。 此情此景&#xff0c;怎么样是不是看懵逼了 不要慌&#xff0c;坐下来耐心听我逼逼赖赖你就会明白这一堆内容是干嘛的 下面介绍每一项分别是…

2022.12.8 半导体器件物理

作业 用迁移电流来推导出公式 电流是I&#xff0c;J是电流密度&#xff0c;W就是横截面 T是这个薄膜的厚度&#xff08;我们最后可以消除掉&#xff09; X就是空间位置&#xff0c;0就是在电极的一端 为什么要减去 因为只有大于阈值电压&#xff0c;才会有水流通过&#xff…

如此简单的时间复杂度计算方法:大O渐进法,你确定不进来康康

对于时间复杂度&#xff0c;空间复杂度&#xff0c;想必这个是大家在学习数据结构的初级阶段就会第一步认识的吧&#xff01;&#xff01;但是&#xff0c;对于复杂度的计算&#xff0c;涉及到了大O渐进法&#xff0c;这个方法是一个笼统的概念&#xff0c;所求得的结果&#x…

大一作业HTML个人网页作业(宠物狗)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

超算对我们的生活有着什么样的影响?

为什么各国都去抢超算的世界第一&#xff1f;因为它对人们的生活有着很大的影响。 一、核武器的成功研发离不开超算 对美国而言&#xff0c;超算的作用首先在研制核武器领域。加利福尼亚的利弗莫尔国家实验室、新墨西哥州的洛斯阿拉莫斯国家实验室、圣地亚国家实验室、橡树岭…

【分立元件】案例:一小批量生产就发现蜂鸣器一致性差?

在文章&#xff1a; 【分立元件】有源蜂鸣器和无源蜂鸣器有什么区别&#xff1f;_阳光宅男李光熠的博客-CSDN博客_有源和无源蜂鸣器 我们讲到有源蜂鸣器内部带震荡源&#xff0c;发声频率固定。无源内部不带震荡源&#xff0c;通过方波去驱动&#xff0c;发音频率可改变。一般…

(Java)SpringMVC学习笔记(一)

前言 今天开始学习SpringMVC&#xff0c;还是跟着尚硅谷视频自学&#xff0c;从两方面把握学习效果&#xff0c;一是知识点理解程度&#xff0c;一是实践程度&#xff0c;而我会将实践具体实现写以成文&#xff0c;用以回顾与分享 JavaWeb知识点简单回顾 JavaBean技术 为了…

订单服务------技术点及亮点

大技术 线程池来实现异步任务&#xff08;亮点1&#xff09; /*** 去结算确认页时封装订单确认页返回需要用的数据* return* throws ExecutionException* throws InterruptedException*/Overridepublic OrderConfirmVo confirmOrder() throws ExecutionException, Interrupted…

小红书推广笔记怎么合作?这里都给大家梳理好啦

截止 2021 年 11 月&#xff0c;小红书月活已达到 2 亿。其中72% 是 90 后群体&#xff0c;50% 的用户在一二线城市。 这些用户流量有着高消费、爱时尚、爱分享、追求品质生活的特点&#xff0c;所以小红书逐渐成为众多品牌方种草推广的必争之地。 小红书推广笔记怎么合作的呢…

FPGA学习笔记(十)IP核之PLL锁相环的学习总结

系列文章目录 一、FPGA学习笔记&#xff08;一&#xff09;入门背景、软件及时钟约束 二、FPGA学习笔记&#xff08;二&#xff09;Verilog语法初步学习(语法篇1) 三、FPGA学习笔记&#xff08;三&#xff09; 流水灯入门FPGA设计流程 四、FPGA学习笔记&#xff08;四&…

pytest自动化测试框架详解+mark标记+fixture夹具

介绍 pytest是python的一种单元测试框架&#xff0c;同自带unittest框架类似&#xff0c;功能&#xff0c;效率更强大 特点&#xff1a; 1. 非常容易上手&#xff0c;入门简单&#xff0c;丰富的文档 2. 支持参数化 3. 执行测试用例的过程中&#xff0c;跳过某些用例&#x…

Python环境搭建

将向大家介绍如何在本地搭建Python开发环境。 Python可应用于多平台包括 Linux 和 Mac OS X。 你可以通过终端窗口输入 "python" 命令来查看本地是否已经安装Python以及Python的安装版本。 Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, SunOS, IRIX, 等等。) W…