【lesson57】信号量和生产者消费者模型(环形队列版)

文章目录

  • 信号量概念
  • 信号量接口
    • 初始化
    • 销毁
    • 等待
    • 发布
  • 基于环形队列的生产者消费者模型
  • 编码
    • Common.h
    • LockGuard.hpp
    • Task.hpp
    • sem.hpp
    • RingQueue.hpp
    • ConProd.cc

信号量概念

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。

什么是信号量?
共享资源—>保证任何时刻都只有一个执行流进行访问----->也就有了之前的临界资源,临界区的概念

之前的互斥加锁—>将共享资源当做整个使用。
但是不同的线程可能访问同一个共享资源的不同区域,这样如何我们加个整体锁必定带来整个进行效率的降低。

如果一个共享资源不当做一个整体,而让不同的执行流访问不同的区域的话,那么不就提高了并发度吗?是的。
而只有当不同的线程访问同一个区域的时候我们再进行同步与互斥。

两个问题
1.我们怎么知道一整个共享区,一共有多少个资源,还剩多少个资源?
2.我们怎么保证这个资源就是给我们的?(程序员编码保证)我们怎么知道线程一定可以具有一个共享资源(信号量保证)

电影院买票的例子:
买票的本质:叫做资源(电影院座位)的预订机制

信号量本质:是一个计数器,访问临界资源的时候,必须先申请资源(sem–预订资源),使用完毕后信号量资源必须释放(sem++)
如何理解信号量的使用—>我们申请一个信号量---->当前执行流一定具有一个资源,可以被它使用—>是哪个资源呢?---->需要程序员结合场景,自定义编码完成。

信号量接口

初始化

int sem_init(sem_t *sem, int pshared, unsigned int value);
//参数:
//pshared:0表示线程间共享,非零表示进程间共享
//value:信号量初始值

销毁

 int sem_destroy(sem_t *sem);

等待

//功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem);

发布

//功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);

基于环形队列的生产者消费者模型

在这里插入图片描述
环形队列采用数组模拟,用模运算来模拟环状特性环形结构起始状态和结束状态都是一样的不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态
但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程
在这里插入图片描述
什么时候生产者和消费者会访问同一个位置?
在这里插入图片描述
1.环形队列空的时候,生产者和消费者会访问同一个位置
2.环形队列满的时候,生产者和消费者会访问同一个位置
3.其它情况不会访问同一个位置。

那么该如何解决?
1.用计数器解决
2.专门浪费一个格子解决

如果生产者和消费者指向了环形结构的同一个位置;那么生产者和消费者要有互斥或者同步的问题,不然生产者和消费者指向的都是不同的位置。

当生产者和消费者指向同一个位置,具有互斥同步关系就可以。
当生产者和消费者不指向同一个位置想让他们并发执行呢?
期望:
1.生产者不能将消费者套圈。
2.消费者超过生产者
3.环形队列为空:一定要让生产者先行
4.环形队列为满:一定要让消费者先行

怎么编码保证?
生产者:最关注的是空间资源
消费者:最关注的是空间里面的数据资源
最开始计数器值:
生产者:spaceSem---->为最大的N
消费者:dataSem---->为0

生产者:生产一个数据
P(spaceSem)—>spaceSem- -
V(dataSem)----->dataSem++
消费者:消费一个数据
P(dataSem)—>dataSem- -
V(spaceSem)----->spaceSem++

编码

Common.h

#pragma once
#include <iostream>
#include <array>
#include <functional>
#include <ctime>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>#define RingQueueNum 5//环形队列空间大小
#define CONSUMRT_NUM 2//生产者个数
#define PRODUCTER_NUM 2//消费者个数
//生产者和消费者个数可以自定义

LockGuard.hpp

#pragma once
#include "Common.h"//自己封装的锁,可以自动初始化和销毁
class Mutex
{
public:Mutex(){pthread_mutex_init(&_mtx,nullptr);}pthread_mutex_t& GetMutex(){return _mtx;}~Mutex(){pthread_mutex_destroy(&_mtx);}
private:pthread_mutex_t _mtx;
};//自己封装RAII风格的lock和unlock,会自动解锁解锁
class LockGuard
{
public:LockGuard(Mutex* mtx):_mtx(mtx){pthread_mutex_lock(&_mtx->GetMutex());//std::cout << "lock" << std::endl;}~LockGuard(){pthread_mutex_unlock(&_mtx->GetMutex());//std::cout << "unlock" << std::endl;}
private:Mutex* _mtx;
};

Task.hpp

#pragma once
#include "Common.h"typedef std::function<int(int, int)> func_t;//自己写的任务
class Task
{
public:Task(){}Task(int x, int y, func_t func): _x(x),_y(y),_func(func){}int operator()(){return _func(_x, _y);}public:int _x;int _y;func_t _func;
};

sem.hpp

#pragma once
#include "Common.h"//自己封装一个信号量P(申请信号量),V(释放信号量)
class Sem
{
public:Sem(int value = RingQueueNum){sem_init(&_sem,0,value);}void P(){sem_wait(&_sem);}void V(){sem_post(&_sem);}~Sem(){sem_destroy(&_sem);}
private:sem_t _sem;
};

RingQueue.hpp

#pragma once
#include "Common.h"
#include "LockGuard.hpp"
#include "Sem.hpp"template<class T>
class RingQueue
{
public:RingQueue():_space_sem(RingQueueNum),_data_sem(0){}void Push(T& in){//先申请信号量_space_sem.P();LockGuard lock(&_producter_mtx);_rq[product_index++] = in;product_index %= RingQueueNum;_data_sem.V();}void Pop(T* out){//先申请信号量_data_sem.P();LockGuard lock(&_cosumer_mtx);*out = _rq[consumer_index++];consumer_index %= RingQueueNum;_space_sem.V();}
private:std::array<T,RingQueueNum> _rq;//环形队列,本质是数组int consumer_index = 0;//消费者数组下标int product_index = 0;//生产者数组下标Mutex _cosumer_mtx;//消费者锁Mutex _producter_mtx;//生产者锁Sem  _space_sem;//空间资源信号量Sem  _data_sem;//数据资源信号量
};

ConProd.cc

#include "Common.h"
#include "RingQueue.hpp"
#include "Task.hpp"int myadd(int x, int y)
{return x + y;
}
void *ConsumerRoutine(void *args)
{RingQueue<Task> *rq = (RingQueue<Task> *)args;while (true){//获取任务Task t;rq->Pop(&t);//执行任务std::cout << pthread_self() << "|Get a Task: " << t._x << " + " << t._y << "=" << t() << std::endl;sleep(1);}
}
void *ProducterRoutine(void *args)
{RingQueue<Task> *rq = (RingQueue<Task> *)args;while (true){//制作任务int x = rand() % 100 + 1;int y = rand() % 100 + 1;Task t(x, y, myadd);std::cout << pthread_self() << "|Make a Task: " << x << " + " << y << "=?" << std::endl;//发送任务rq->Push(t);sleep(1);}
}
int main()
{srand((unsigned int)time(nullptr) ^ getpid() ^ 0x77777);//定义线程pthread_t consumer[CONSUMRT_NUM];pthread_t producter[PRODUCTER_NUM];//定义环形队列RingQueue<Task> *ring_queue = new RingQueue<Task>;//创建消费者线程for (int i = 0; i < CONSUMRT_NUM; i++){pthread_create(consumer + i, nullptr, ConsumerRoutine, (void *)ring_queue);}//创建生产者线程for (int i = 0; i < PRODUCTER_NUM; i++){pthread_create(producter + i, nullptr, ProducterRoutine, (void *)ring_queue);}//等待消费者线程for (int i = 0; i < CONSUMRT_NUM; i++){pthread_join(consumer[i], nullptr);}//等待生产者线程for (int i = 0; i < PRODUCTER_NUM; i++){pthread_join(producter[i], nullptr);}return 0;
}

多生产和多消费的意义在哪?
提高线程的并发度。
信号量的本质是一个计数器—>计数器的意义是什么?
以前:申请锁—>判断与访问---->释放锁—>本质我们是不清楚临界资源的情况
现在:信号量要提前预定资源的情况,而在PV变化过程中,我们可以在外部就能知晓临界资源的情况。

计数器的意义:可以不用进入临界区,就可以得知临界资源的情况,甚至可以减少内部资源的判断。

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

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

相关文章

【测试】JUnit

目 录 一.注解二.断言三.用例的执行顺序四.参数化五.测试套件 自动化就是 selenium 脚本来实现的 junit 是 java 的单亓测试工具&#xff0c;只不过我们在实现自动化的时候需要借用一下下 junit 库里面提供的一些方法 引入依赖 Junit 5 <!-- https://mvnrepository.com/a…

自然语言编程系列(二):自然语言处理(NLP)、编程语言处理(PPL)和GitHub Copilot X

编程语言处理的核心是计算机如何理解和执行预定义的人工语言&#xff08;编程语言&#xff09;&#xff0c;而自然语言处理则是研究如何使计算机理解并生成非正式、多样化的自然语言。GPT-4.0作为自然语言处理技术的最新迭代&#xff0c;其编程语言处理能力相较于前代模型有了显…

电子元器件基础5---二极管

除了电阻、电容和电感等线性元器件之外,还有二极管、三极管这些常用的非线性器件广泛应用于日常生活中。那么今天我们来介绍以下二极管这一常用的电子元器件。 一、二极管概念 二极管是用半导体材料(硅、硒、锗等)制成的一种电子器件 。二极管有两个电极,正极,又叫阳极;负…

django报错:Cannot use ImageField because Pillow is not installed

1、问题概述 ERRORS: accounts.User.avatar: (fields.E210) Cannot use ImageField because Pillow is not installed. HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "python -m pip install Pillow". System check identified 1 …

JDK1.8安装教程

目录 下载安装环境配置打开系统高级设置环境配置 验证安装是否成功 下载 https://www.oracle.com/java/technologies/downloads/#java8-windows 安装 打开安装包&#xff0c;点击下一步。 选择好自己熟悉的目的安装目录&#xff0c;点击下一步。 等待安装 选择好jre的安装目…

ubuntu22.04@laptop OpenCV Get Started: 013_contour_detection

ubuntu22.04laptop OpenCV Get Started: 013_contour_detection 1. 源由2. 应用Demo2.1 C应用Demo2.2 Python应用Demo 3. contour_approx应用3.1 读取图像并将其转换为灰度格式3.2 应用二进制阈值过滤算法3.3 查找对象轮廓3.4 绘制对象轮廓3.5 效果3.6 CHAIN_APPROX_SIMPLE v.s…

java中的枚举

枚举 枚举类型的概述 关键字&#xff1a;enum 你可以把枚举类型理解成是一个自定义的常量的序列 枚举的语法结构 定义的枚举类型文件 package com.it.xiaosi.demo01;/*** Classname : direction* Description : TODO 枚举* Author : lin_refuelqq.com*/ public enum direct…

springboot第56集:微服务框架,物联网IOT,SQL数据库MySQL底层,AOP收集业务操作日志架构周刊...

单点登录 1.配置代理信息 /*请求登陆的方法*/ "/modelLogin": {//本地服务接口地址&#xff0c;这是测试环境&#xff0c;正式环境需要更改下地址target: "http://127.0.0.1:6776/xxx-auth/",changeOrigin: true,pathRewrite: {"^/modelLogin": …

在ubuntu中制作ubuntu的U盘启动盘

概要&#xff1a; 本篇演示在ubuntu22.04中制作ubuntu22.04的U盘启动盘 一、下载ubuntu22.04的iso文件 访问ubuntu官网https://ubuntu.com自行下载ubuntu官网 二、制作U盘启动盘 打开系统自带软件Startup Disk Creator 软件会自动检测iso文件和U盘 点击Make Startup Disk…

【Java EE初阶十二】网络原理(二)

2. 传输层 2.2 TCP协议 2.2.2 关于可靠传输 4.滑动窗口 前面的三个机制&#xff0c;都是在保证 tcp 的可靠性&#xff1b; TCP 的可靠传输,是会影响传输的效率的.(多出了一些等待 ack 的时间,单位时间内能传输的数据就少了)&#xff1b; 滑动窗口,就让可靠传输对性能的影响,更…

MySQL数据库⑪_C/C++连接MySQL_发送请求

目录 1. 下载库文件 2. 使用库 3. 链接MySQL函数 4. C/C链接示例 5. 发送SQL请求 6. 获取查询结果 本篇完。 1. 下载库文件 要使用C/C连接MySQL&#xff0c;需要使用MySQL官网提供的库。 进入MySQL官网选择适合自己平台的mysql connect库&#xff0c;然后点击下载就行…

Day-02-02

Httpclient测试 安装HTTP Client插件 使用IDEA自带的http接口测试工具——HTTP Client Open in HTTP Client 生成测试用例 点击绿色箭头可以运行测试用例&#xff0c;控制台会输出结果。 保存和修改测试用例 在模块下新建一个api-test包用来存放测试用例&#xff0c;将生…

Packet content transfer stopped (received 8 bytes)

esp32烧录程序时报错&#xff1a;A fatal error occurred: Packet content transfer stopped (received 8 bytes) 解决方法&#xff1a; 降低上传速度&#xff0c;使用115200&#xff1b;更换flash模式&#xff0c;使用DIO方式重试如果还不行&#xff0c;检查flash连接情况&am…

failing dimm dimm location (uncorrectable memory component found)

开机界面出现这个 failing dimm dimm location (uncorrectable memory component found)“DIMM DIMM location. Uncorrectable memory component found” 是一条硬件故障信息&#xff0c;表明在服务器的某个特定双列直插式内存模块&#xff08;Dual In-line Memory Module, DI…

【漏洞复现】蓝网科技临床浏览系统信息泄露漏洞

Nx01 产品简介 蓝网科技临床浏览系统是一个专门用于医疗行业的软件系统&#xff0c;主要用于医生、护士和其他医疗专业人员在临床工作中进行信息浏览、查询和管理。 Nx02 漏洞描述 蓝网科技临床浏览系统存在信息泄露漏洞&#xff0c;攻击者可以利用该漏洞获取敏感信息。 Nx03…

深度学习基础之《TensorFlow框架(4)—Operation》

一、常见的OP 1、举例 类型实例标量运算add&#xff0c;sub&#xff0c;mul&#xff0c;div&#xff0c;exp&#xff0c;log&#xff0c;greater&#xff0c;less&#xff0c;equal向量运算concat&#xff0c;slice&#xff0c;splot&#xff0c;canstant&#xff0c;rank&am…

力扣72. 编辑距离(动态规划)

Problem: 72. 编辑距离 文章目录 题目描述思路复杂度Code 题目描述 思路 由于易得将字符串word1向word2转换和word2向word1转换是等效的&#xff0c;则我们假定统一为word1向word2转换&#xff01;&#xff01;&#xff01; 1.确定状态&#xff1a;我们假设现在有下标i&#x…

Python算法题集_随机链表的复制

Python算法题集_随机链表的复制 题138&#xff1a;随机链表的复制1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【双层循环】2) 改进版一【字典哈希】3) 改进版二【单层哈希】4) 改进版三【递归大法】 4. 最优算法 本文为Python算法题集之一的…

Netty基本组件

编解码器 编码器 类作用MessageToByteEncoder将消息编码为字节MessageToMessageEncoder将一种消息编码为另一种消息 解码器 类作用ByteToMessageDecoder将字节解码为消息MessageToMessageDecoder将一种消息解码为另一种消息

4 月 9 日至 4 月 10 日,Hack.Summit() 2024 首聚香江

Hack.Summit() 是一系列 Web3 开发者大会。2024 年的活动将于 2024 年 4 月 9 日至 4 月 10 日在香港数码港举行。自十年前首次举办以来&#xff0c;此次会议标志着 Hack.Summit() 首次在亚洲举办&#xff0c;香港被选为首次亚洲主办城市&#xff0c;这对 Hack VC 和该地区都具…