互斥锁完成

#include <iostream>
#include <deque>
#include <thread>
#include <mutex>std::deque<int> q;
std::mutex mtx;static void produce(int val) {while(val--) {std::unique_lock<std::mutex> guard(mtx);q.push_front(val);mtx.unlock();std::this_thread::sleep_for(std::chrono::seconds(1));}
}
static void consumer() {int data = INT_MAX;while(data != 0) {std::unique_lock<std::mutex> guard(mtx);if (!q.empty()) {data = q.back();q.pop_back();std::cout << data << std::endl;mtx.unlock();} else  {mtx.unlock();}}
}
void test() {std::thread t1(produce,3);std::thread t2(consumer);t1.join();t2.join();
}int main() {test();return 0;
}

效果如下:

9
8
7
6
5
4
3
2
1
0
Process finished with exit code 1

produce在生产过程中,std::this_thread::sleep_for (std::chrono::seconds(1));表示延时1s,所以生产过程很慢。
consumer存在着一个while循环,只有在接收到表示结束的数据的时候,才会停止,每次循环内部,都是先加锁,判断队列不空,然后就取出一个数,最后解锁。这样其实做了很多无用功,并且CPU占用率很高
可以在consumer内部也加一个小延时,在一次判断后,如果发现队列是空的,那就惩罚一下自己,延时一下,减少CPU的占用率。

static void consumer() {int data = INT_MAX;while(data != 0) {std::unique_lock<std::mutex> guard(mtx);if (!q.empty()) {data = q.back();q.pop_back();std::cout << data << std::endl;mtx.unlock();} else {mtx.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(500));}}
}

条件变量改进模型

c++11提供了#include <condition_variable>头文件,std::condition_variable可以和std::mutex结合一起使用,其中有两个重要的接口,notify_one()wait()
wait()可以让线程陷入休眠状态,在消费者生产者模型中,如果生产者发现队列中没有东西,就可以让自己休眠.notify_one()就是唤醒处于wait中的其中一个条件变量.
那什么时刻使用notify_one()比较好呢,当然是在生产者往队列中放数据的时候了,队列中有数据,就可以赶紧叫醒等待中的线程起来干活了。
下面是主要修改代码:

std::condition_variable cond;static void produce(int val) {while(val--) {std::unique_lock<std::mutex> guard(mtx);q.push_front(val);mtx.unlock();cond.notify_one();  // 提醒一个waiting的线程std::this_thread::sleep_for(std::chrono::seconds(1));}
}
static void consumer() {int data = INT_MAX;while(data != 0) {std::unique_lock<std::mutex> guard(mtx);// 如果队列为空,就一直等直到被notify_one唤醒while(q.empty())cond.wait(guard);data = q.back();q.pop_back();mtx.unlock();std::cout << data << std::endl;}
}

此时CPU的占用率也很低,因为在消费者端,队列为空时,将控制权交给了cpu,直到被唤醒。
需要注意的是在判断队列是否为空的时候,使用的是while(q.empty()),而不是if(q.empty())
这是因为wait()从阻塞到返回,不一定就是由于notify_one()函数造成的,还有可能由于系统的不确定原因唤醒(可能和条件变量的实现机制有关),这个的时机和频率都是不确定的,被称作伪唤醒,如果在错误的时候被唤醒了,执行后面的语句就会错误,所以需要再次判断队列是否为空,如果还是为空,就继续wait()阻塞。
在管理互斥锁的时候,使用的是std::unique_lock而不是std::lock_guard,在上一篇笔记C++多线程快速入门(二)共享数据同步以及数据竞争中,谈到过ock_guard没有lock和unlock接口,而unique_lock提供了。这里的话也是由于此点原因。因为在wait()函数之前,使用互斥锁保护了,如果wait的时候什么都没做,岂不是一直持有互斥锁?那生产者也会一直卡住,不能够将数据放入队列中了。所以,wait()函数会先调用互斥锁的unlock()函数,然后再将自己睡眠,在被唤醒后,又会继续持有锁,保护后面的队列操作。
另外除了notify_one()函数,c++还提供了notify_all()函数,可以同时唤醒所有处于wait状态的条件变量。

参考

https://blog.csdn.net/qq_43145072/article/details/103732176

往期内容回顾

C++多线程快速入门(二)共享数据同步以及数据竞争
C++多线程快速入门(一):基本&常用操作

C++多线程快速入门(三):生产者消费者模型与条件变量使用相关推荐

  1. 2022-2-3 牛客C++项目 —— 生产者消费者模型(条件变量、信号量)

    如果生产者将容器填满,需要通知消费者, 如果消费者将容器清空,需要通知生产者. (我个人觉得如果有多个生产者和消费者,就排成队列,通知第一个就行.不知道实际情况是什么样子的) /* 生产者消费者模型( ...

  2. 生产者消费者模型(条件变量)

    三种关系:互斥,同步,互斥和同步 两类角色:生产者,消费者(线程) 一个交易场所:生产者消费者共享的区域 卖苹果的模型 dish上面只有一个苹果 买家必须要等卖家把苹果放到dish上才可以去买苹果. ...

  3. 3.12-3.14生产者和消费者模型、条件变量、信号量

    目录 1.生产者消费者模型 2.条件变量 3.信号量 1.生产者消费者模型 2.条件变量 3.信号量 初始化的值:生产者-目前可以生产几个 -n   消费者-目前可以消费几个 -0

  4. 【Linux入门】多线程(线程概念、生产者消费者模型、消息队列、线程池)万字解说

    目录 1️⃣线程概念 什么是线程 线程的优点 线程的缺点 线程异常 线程异常 Linux进程VS线程 2️⃣线程控制 创建线程 获取线程的id 线程终止 等待线程 线程分离 3️⃣线程互斥 进程线程间 ...

  5. Linux 多线程编程(实现生产者消费者模型)

    Linux 多线程编程 线程分类 线程按照其调度者可以分为用户级线程和内核级线程两种. 内核级线程 在一个系统上实现线程模型的方式有好几种,因内核和用户空间提供的支持而有一定程度的级别差异.最简单的模 ...

  6. python生产教程_python入门教程12-09 (python语法入门之生产者消费者模型)

    Python中的生产者消费者模型,在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题,是本章的重点内容,下面我们一起来看看吧. 生产者消费者模型 在并发编程中使用生产者和消费者模式能够解决绝大 ...

  7. AS3多线程快速入门(三):NAPE物理引擎+Starling

    原文:http://blog.domlib.com/articles/345 [更新]Adobe在11.4正式发布的最后一刻移除了ByteArray.shareable功能的支持,推迟到11.5版本再 ...

  8. AS3多线程快速入门(三):NAPE物理引擎+Starling[译]

    原文链接:http://esdot.ca/site/2012/intro-to-as3-workers-part-3-nape-physics-starling [更新]Adobe在11.4正式发布的 ...

  9. C++多线程并发(三)---线程同步之条件变量

    文章目录 一.何为条件变量 二.为何引入条件变量 三.如何使用条件变量 更多文章: 一.何为条件变量 在前一篇文章<C++多线程并发(二)-线程同步之互斥锁>中解释了线程同步的原理和实现, ...

最新文章

  1. PHP 不跳转界面取input值进行验证_【Python】tesseract+uiautomator2+夜神模拟器 悠长假期手游集市识别验证码自动购买 - Amorius...
  2. cookie被淘汰_session正在被淘汰吗?
  3. windows不能改密码
  4. 安装EPP的调试Zend Debugger
  5. 可以抛弃 Python 了?Google 开源 Swift for TensorFlow 意味着什么
  6. 1032. Sharing (25)-PAT甲级真题
  7. 理解:回归与拟合、归一化与标准化
  8. python函数abs()
  9. Android 功能系列篇
  10. 【Hadoop】Hadoop大数据架构及关键组件
  11. LSTM神经网络在证券市场分析上的应用
  12. 【第105期】游戏策划:应聘简历的附件那些事
  13. 数据线CE测试标准 准备资料
  14. Unity3D好用Unity模型场景素材和Unity资源大合集
  15. EI国际会议计算机2018,【Ei检索】2018年自动控制,机电和工业工程国际会议
  16. 开幕在即!众星齐聚八泉峡推介会 为山西加油,为二青助力!
  17. Flask懒加载时 moles.py 无法运行
  18. leetcode:954. 二倍数对数组
  19. 2021年山阳中学高考成绩查询,陕西省山阳中学2018年高考成绩公布,2018年高考再创辉煌!...
  20. 地理空间索引:线段与多边形的GeoHash编码

热门文章

  1. 域名如何设置才能带www和不带www都能正常访问
  2. 毛绒材质渲染_学室内设计必进,建模渲染那都不是事儿
  3. 服务器物理部署拓扑图,【网络】叶脊(Spine-Leaf)网络拓扑下全三层网络设计与实践(五)-物理服务器路由方案及配置...
  4. java 静态方法与实例方法的区别_静态方法与实例方法的区分
  5. angularjs的$http请求方式
  6. response对象的使用
  7. P4336 [SHOI2016]黑暗前的幻想乡
  8. JavaScript:Browser 对象
  9. MarkDownPad2 注册码
  10. TCP、UDP套接字的数据传输