设计并实现一个进程,该进程拥有一个生产者线程和一个消费者线程,它们使用N个不同的缓冲区(N为一个确定的数值,例如N=32)。需要使用如下信号量:
一个互斥信号量,用以阻止生产者线程和消费者线程同时操作缓冲区列表;
一个信号量,当生产者线程生产出一个物品时可以用它向消费者线程发出信号;
一个信号量,消费者线程释放出一个空缓冲区时可以用它向生产者线程发出信号。

主要程序结构

#include<iostream>
#include<Windows.h>
#include<process.h>
#include<vector>
using namespace std;// 等价于WINAPI,约定使用stdcall函数调用,既被调用者负责清栈
#define STD __stdcall
// 确定缓冲区大小,这里选取5,便于展示和说明结果
#define LENGTH 5
// 随机时间
#define GETMYRAND() (int)(((double)rand()/(double)RAND_MAX)*300)// 使用临界区来同步线程
CRITICAL_SECTION _cr;
//空信号量
HANDLE emptySemaphore = NULL;
//满信号量
HANDLE fullSemaphore = NULL;
// 缓冲区Buffer
vector<int> buffer;// 消费者线程
DWORD STD Consumer(void* lp) {
while(true) {//等待判断缓冲区满的信号量WaitForSingleObject(fullSemaphore,0xFFFFFFFF);//进入临界区,线程同步,功能同互斥量 EnterCriticalSection(&_cr);//消费者线程从缓冲区中取出消费一个资源buffer.pop_back();//打印当前缓冲区可用资源数cout << "消费者消费一个资源,当前可用资源数:" << buffer.size() << endl;//离开临界区LeaveCriticalSection(&_cr);//释放判断缓冲区空的信号量ReleaseSemaphore(emptySemaphore,1,NULL);//线程睡眠随机时间Sleep(GETMYRAND());
}
return 0;
}// 生产者线程
DWORD STD Producer(void* lp) {
while(true){//等待判断缓冲区空的信号量WaitForSingleObject(emptySemaphore, 0xFFFFFFFF);//进入临界区,线程同步,功能同互斥量 EnterCriticalSection(&_cr);//生产者线程向缓冲区中生成一个资源buffer.push_back(1);//打印当前缓冲区可用资源数cout << "生产者新生产一个资源,当前可用资源数:" << buffer.size() << endl;//离开临界区LeaveCriticalSection(&_cr);//释放判断缓冲区满的信号量ReleaseSemaphore(fullSemaphore, 1, NULL);//线程睡眠随机时间Sleep(GETMYRAND());
}return 0;
}int main() {//创建信号量
emptySemaphore = CreateSemaphore(NULL, LENGTH, LENGTH, NULL);
fullSemaphore = CreateSemaphore(NULL, 0, LENGTH, NULL);//初始化临界区
InitializeCriticalSection(&_cr);
//开启多线程
HANDLE handles[2];
handles[1] = CreateThread(0, 0, &Producer, 0, 0, 0);
handles[0] = CreateThread(0, 0, &Consumer,  0, 0, 0);//等待子线程执行完毕
WaitForMultipleObjects(2, handles, true, INFINITE); //"Join" trreads
//释放子线程
CloseHandle(handles[0]);
CloseHandle(handles[1]);
//释放临界区
DeleteCriticalSection(&_cr);
return 0;
}

一、生产者消费者线程互斥

使用critical area(临界区)来保证生产者和消费者的线程彼此互斥:

图片.png

在多线程开启前后分别初始化临界区和销毁临界区。
接下来,在生产者线程函数和消费者线程函数对缓冲区进行操作的代码前后启用临界区和离开临界区,来做到两个线程间互斥同步。

图片.png

图片.png

最终效果是,虽然两个线程都通过死循环不断执行循环体中代码,但是在其中一个线程执行临界区代码时,另一个线程被互斥阻塞。

图片.png

二、生产者、消费者分别在“满”和“空”时阻塞

生产者线程得到emptySemaphore信号量时,其计数器-1,开始生产资源。在生产结束后,使fullSemaphore的计数器+1。
消费者线程得到fullSemaphore信号量时,其计数器-1,开始生产资源。在生产结束后,使emptySemaphore的计数器+1。
通过上面所述过程,使用emptySemaphore和fullSemaphore两个信号量可以做到资源生产满时生产者阻塞,资源消费完时消费者阻塞的功能。

图片.png

图片.png

为了演示消费者阻塞效果,我们把生产者的生产效率降低,既在生产者线程结束后,Sleep睡眠的时间延长。

图片.png

查看效果:

图片.png

每隔大半秒出现一对生产者和消费者记录,说明资源不足时,已经阻塞消费者继续消费资源,而等待生产者Sleep休眠时间之后,生产出新的资源,消费者才继续消费。
为了演示生产者阻塞效果,我们把消费者的消费效率降低,既在消费者线程结束后,Sleep睡眠的时间延长。

图片.png

查看效果:

图片.png

当资源数到达5时,资源已满时,既阻塞生产者继续生产资源,而等待消费者Sleep休眠时间之后,消费掉资源,生产者才继续生产。

查阅资料及笔记:

在生产者和消费者的代码中经常出现DWORD WINAPI
其中,WINAPI是宏定义 #define WINAPI __stdcall
stdcall是一种函数调用约定,其清栈工作由被调用者执行,对于节省内存效果很好,但缺点是参数不是可变长类型。
另外还有__cdcel、__fastcall等。
临界区、互斥量、信号量、事件总结:
1. 互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量 。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它。
临界区的缺点是:没有办法知道进入临界区中的那个线程是生是死。如果那个线程在进入临界区后当掉了,而且没有退出来,那么系统就没有办法消除掉此临界区。
2. 互斥量(Mutex),信号量(Semaphore),事件(Event)都可以被跨越进程使用来进行同步数据操作,而其他的对象与数据同步操作无关,但对于进程和线程来讲,如果进程和线程在运行状态则为无信号状态,在退出后为有信号状态。所以可以使用WaitForSingleObject来等待进程和 线程退出。
3. 通过互斥量可以指定资源被独占的方式使用,但如果有下面一种情况通过互斥量就无法处理,比如现在一位用户购买了一份三个并发访问许可的数据库系统,可以根据用户购买的访问许可数量来决定有多少个线程/进程能同时进行数据库操作,这时候如果利用互斥量就没有办法完成这个要求,信号灯对象可以说是一种资源计数器。
mutex和samephore都是信号量,但前者实现critical area的功能,后者带计数器。mutex可以说是计数为1的samephore。

作者:日生三金
链接:https://www.jianshu.com/p/2216ecba94ea
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

C++ 生产者消费者模式相关推荐

  1. 【C++】多线程(链式、循环队列)实现生产者消费者模式

    生产者消费者模式:         生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同 ...

  2. 面试官让我手写一个生产者消费者模式?

    不知道你是否遇到过面试官让你手写生产者消费者代码.别说,前段时间有小伙伴还真的遇到了这种情况,当时是一脸懵逼. 但是,俗话说,从哪里跌倒就要从哪里爬起来.既然这次被问到了,那就回去好好研究一下,争取下 ...

  3. java lock condition_Java 通过 Lock 和 竞争条件 Condition 实现生产者消费者模式

    更多 Java 并发编程方面的文章,请参见文集<Java 并发编程> 竞争条件 多个线程共享对某些变量的访问,其最后结果取决于哪个线程偶然在竞争中获胜. condition.await() ...

  4. 生产者/消费者模式(阻塞队列)

    生产消费者模式  貌似也是阻塞的问题  花了一些时间终于弄明白这个鸟东东,以前还以为是不复杂的一个东西的,以前一直以为和观察者模式差不多(其实也是差不多的,呵呵),生产消费者模式应该是可以通过观察者模 ...

  5. 多线程终极模式:生产者-消费者模式

    多线程de小事情 导航不迷路: 程序.进程以及线程的爱恨情仇 最简单实现多线程的方法(Thread) 简单易懂的多线程(通过实现Runnable接口实现多线程) 常用获取线程基本信息的方法(新手专属) ...

  6. Java中设计模式之生产者消费者模式-3

    引言 生产者-消费者(producer-consumer)问题,也称作有界缓冲区(bounded-buffer)问题,两个进程共享一个公共的固定大小的缓冲区.其中一个是生产者,用于将消息放入缓冲区:另 ...

  7. 单线程下的生产者--消费者模式详解,wait和sleep的区别

    1. 单线程下的生产者--消费者模式 1.1 该模式下,一个线程生产数据,另一个线程处理数据.当数据还没被处理,那么生产数据的线程进入等待状态:如果数据还没生产,那么处理数据的线程进入等待状态,代码及 ...

  8. java消费者模式_基于Java 生产者消费者模式(详细分析)

    生产者消费者模式是多线程中最为常见的模式:生产者线程(一个或多个)生成面包放进篮子里(集合或数组),同时,消费者线程(一个或多个)从篮子里(集合或数组)取出面包消耗.虽然它们任务不同,但处理的资源是相 ...

  9. [19/04/11-星期四] 多线程_并发协作(生产者/消费者模式_2种解决方案(管程法和信号灯法))...

    一.概念 多线程环境下,我们经常需要多个线程的并发和协作.这个时候,就需要了解一个重要的多线程并发协作模型"生产者/消费者模式". Ø 什么是生产者? 生产者指的是负责生产数据的模 ...

  10. Java并发程序设计(十一)设计模式与并发之生产者-消费者模式

    设计模式与并发之生产者-消费者模式 生产者-消费者模式是一个经典的多线程设计模式.它为多线程间的协作提供了良好的解决方案. 在生产者-消费者模式中,通常由两类线程,即若干个生产者线程和若干个消费者线程 ...

最新文章

  1. 用boson做vlan的单臂路由实验
  2. 树莓派python编程小车_python3实现网页版raspberry pi(树莓派)小车控制
  3. wpf中groupbox有什么用_环境中的硫化氢用什么检测好
  4. 如何查看某个用户指定时间段的ABAP开发记录
  5. 位枚举(Bit Flags)
  6. [BZOJ5286][HNOI2018]转盘(线段树)
  7. sap产品图谱 - road to sap.pdf_蛇胆陈皮胶囊化学成分及指纹图谱研究
  8. php如何删除zip文件内容,删除php中的zip文件夹
  9. 【深入浅出imx8企业级开发实战 | 01】imx8qxp yocto工程构建指南
  10. 【金猿技术展】PLC电力载波通信技术——电力系统特有通信方式
  11. C# MES系统结构梳理
  12. 关于医学影像中的轴位面(横断面)、冠状面、矢状面的解释(转载)
  13. 阿里云、腾讯云、UCloud、华为云云主机对比测试报告
  14. 免费还能商用的视频素材,拿走不谢。
  15. phpstudy php56 zend,关于shopex 4.9 php5.6版安装环境问题
  16. 大厂面试快问快答,10分钟搞定MySQL夺命20问,你都能接住吗?
  17. 计算机主机自动关机如何设置,电脑设置如何自动关机【图文教程】
  18. 计算机桌面颜色怎么换,电脑桌面字体颜色怎么改
  19. GitHub 下载神器强势回归!
  20. 回顾敏捷实践踩过的坑:如果重新做,我会这样做(一)

热门文章

  1. Application类的使用方法
  2. 什么是缓冲区溢出以及如何利用漏洞?
  3. MyBatis-Spring-TransactionManager
  4. matlab自带滤波器,数字滤波 - MATLAB Simulink - MathWorks 中国
  5. 杭电acm第2304题答案c语言,【转】杭电ACM试题分类
  6. coderwhy老师vue.js b站视频教程笔记——第一部分
  7. 【定量分析、量化金融与统计学】统计推断基础(5)---一类错误与二类错误
  8. ASP.NET CodeBehind
  9. PE文件-C++-MFC-IDA-逆向分析-x32dbg
  10. linux shell chmod,Shell chmod 命令简介