基于信号量的生产者消费者模型

news2024/10/1 20:14:36

文章目录

  • 信号量
    • 认识概念
    • 基于线程分析信号量
    • 信号量操作
  • 循环队列下的生产者消费者模型
    • 理论认识
    • 代码部分

信号量

认识概念

信号量本质:
计数器
它也叫做公共资源

为了线程之间,进程间通信------>多个执行流看到的同一份资源---->多个资源都会并发访问这个资源(此时易出现覆盖)---->数据不一致的问题----->保护起来---->互斥 和 同步

总结:解决问题的同时会伴随新问题的发生
互斥:任何时候只允许一个执行流(进程)访问公共资源,加锁完成
同步:多个执行流执行的时候,按照一定的顺序执行

被保护起来的资源,临界资源(如,管道)<------->非临界资源
访问该临界资源的代码叫做临界区<----->非临界区

维护临界资源就是维护临界区

原子性:只有两态,对于一件事要么没做要么就做完了(操作系统中)

如何理解信号量?
看电影为例,买票座位属于你,还是坐上去座位就属于你?
但有人占座,这个时候电影院内部的座位,就是多人共享的资源------公共资源
买票的本质: 是对资源的预订机制

有100个座位就绝对不会卖出101张票,如何做到这一点?
	维护一个计数器,
	int count = 100;//表示公共资源的个数

在这里插入图片描述
信号量:表示对资源数目的计数器,每一个执行流系想访问公共资源内的谋一份资源,不应该让执行流直接访问
而是先申请信号量资源.先对信号量计数器进行–操作,只要–成功了,就完成了对资源的预定机制
如果申请不成功,执行流被挂起阻塞

对于公共资源,在他的前面添加
在这里插入图片描述对于信号量为1的公共资源
int sem=1;
二元信号量—互斥锁—完成互斥功能
只能有一个人成功 ,其他执行流(进程)访问会出现阻塞

这样的表明允许访问公共资源的计数叫做信号量(信号灯)

分析细节问题:

1.每个进程都要看到同一个信号量资源-----就只能由OS提供,在IPC体系

2.信号量本质也是公共资源(还好信号量的访问并不复杂,只有-- ++ 的操作,如果访问出错,只要阻塞就可以)
这个操作也属于原子性
- - P
++ V
为什么不定义一个int来完成这个操作?
访问数据的不同进程间的操作,包含大量的拷贝等工作,所以单靠一个int不行

3.单个信号量(目前就这么理解)
struct sem
{
int count;//计数器
task_struct *wait_queue;//等待队列,对这个队列里面的进程进行操作(类似需要操作就对他进行)
}

基于线程分析信号量

现在基于线程概念再来整体理解一下:

1.信号量的本质是一把计数器
2.申请信号的本质就是预订资源
3.pv操作是原子的

怎么理解?

在上篇写的阻塞队列是一个公共资源,同时他的访问是一个整体形式去访问
现在假设这个公共资源是一个数组,那么不同线程可以访问不同数组的不同索引由此达到共同访问临界资源的目的.假设这个数组就是大小为7,有8个线程访问这个临界资源就会出现问题,所以这个时信号量就会起作用,他就相当于是一个计数器

所以上述情况,线程在访问之前都会先申请一个信号量,然后访问指定的一个位置(程序员进行维护),访问结束,释放信号量

这时,对于资源的访问判断已经由信号量充当了,所以不需要进行资源就绪的判断信号量申请成功,这个资源就一定能访问,这个也是原子性的
信号量为1,表示这个资源整体就是只能一个线程进行访问,这边就是互斥的

信号量操作

信号量的操作:
1.快速认识接口
信号量创建:

在这里插入图片描述

参数1:定义一个类似pthread_t类型的变量,这个类型在这边是sem_t类型,
参数2:在一个进程的线程之间共享(0表示这个种情况)还是在进程之间共享
参数3:这个信号量计数器的初始值
信号量销毁:

在这里插入图片描述
销毁信号量
信号量申请,也可以说是信号量等待(阻塞等待,有信号量才会进行后续操作,信号量的值减1)

在这里插入图片描述
这边只讨论普通阻塞方式
发布信号量:

在这里插入图片描述
表示信号量资源使用完毕,可以归还资源,将信号量的值+1

循环队列下的生产者消费者模型

理论认识

基于环形队列的生产者消费者问题–理论
环形队列:
逻辑上为环状,入队列出队列为同一个位置
判空判满可以加一个计数器或者是消耗一个空间的方式进行
这边不做讨论,因为信号量的操作就能完成判定
在这里插入图片描述
在循环队列中,

1.生产者不能把消费者套一个圈
2.消费者不能超过生产者

消费者生产者在这个结构中也是一样,为空或者为满会指向同一个位置,这个时候就不能并发访问,在其他情况下可以去并发访问,也就是并发进入临界区.

所以在为空 为满为互斥,生产者 消费者跑是同步,这是需要局部维持的"资源"的认识:
P:空间是资源(无空间了,不生产)
c:数据是资源(无数据了,不消费)
所以需要两个信号量来维护这个资源

伪代码:

p->sem_space:N
c->sem_data:0
生产者:
	p(sem_space)//预定资源,申请空间资源用来生产
	//生产行为,位置占据
	v(sem_data)//告诉消费者可以进行消费		
消费者:
	p(sem_data)//申请数据资源用来消费
	//消费行为
	v(sem_space)//告诉生产者当前位置为空可以来生产

代码部分

单任务的循环队列式生产者消费者模型
main.cc

#include "RingQueue.hpp"
#include <unistd.h>
void *Productor(void *args)
{
    RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
    int cnt = 100;
    while(true)
    {
        rq->Push(cnt);
        std::cout << "Productor done, the data is " << cnt << std::endl;
        cnt--;
    }
}
void *Consumer(void *args)
{
    RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
    while(true)
    {
        int data = 0;
        rq->Pop(&data);
        std::cout << "Consumer done, the data is " << data << std::endl;
        sleep(1);
    }
    
}
int main()
{
    pthread_t c, p;
    RingQueue<int> *rq = new RingQueue<int>();
    pthread_create(&p, nullptr, Productor, rq);
    pthread_create(&c, nullptr, Consumer, rq);   

    pthread_join(p, nullptr);
    pthread_join(c, nullptr);


    return 0;
}

RingQueue.hpp

#pragma once
#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>

const int defaultsize = 5;
template<class T>
class RingQueue
{
private:
    void P(sem_t &sem)//用于申请可以访问的资源
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)//资源使用完毕
    {
        sem_post(&sem);
    }
public:
    RingQueue(int size = defaultsize):_ringqueue(size), _size(size), _p_step(0), _c_step(0)
    {
        sem_init(&_space_sem, 0, size);
        sem_init(&_data_sem, 0, 0);
    }
    void Push(const T& in)//生产者放入数据
    {
        P(_space_sem);
        _ringqueue[_p_step] = in;
        _p_step++;
        _p_step %= _size;
        V(_data_sem);
    }
    void Pop(T *out)
    {
        P(_data_sem);
        *out = _ringqueue[_c_step];
        _c_step++;
        _c_step %= _size;
        V(_space_sem);
    }
    ~RingQueue()
    {
        sem_destroy(&_space_sem);
        sem_destroy(&_data_sem);
    }
private:
    std::vector<T> _ringqueue;
    int _size;

    sem_t _space_sem;
    sem_t _data_sem; 

    int _p_step;
    int _c_step;
};

引入任务后的生产者消费者模型
在原来基础上,将处理数据从int变为实际的Task,这边的Task只是一个算术运算,实际需求可根据实际情况去更改
在这里插入图片描述

即 将模板参数和生产者 消费者的实际内容进行修改
多生产 多消费内容的修改

与单生产,单消费的区别在于需要考虑生产者与生产者之间,消费者与消费者之间的关系
利用加锁的方式,让消费者之间可以进行不冲突的添加任务,消费者也是如此

所以在普遍情况下,各个生产者之间,消费者之间是互斥关系
先加锁还是先申请信号量?

答案是先分配信号量再申请锁,这样比较快,这就好比是先买票再排队,而不是排队到你之后再买票,后面的人还要等你买票,时间消耗大

main函数内部:
在这里插入图片描述productor和consumer

在这里插入图片描述
这样一个基于信号量的多线程生产者消费者任务就完成了.

关注我,虾片更精彩~~

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

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

相关文章

【Qt课设】基于Qt实现的中国象棋

一、摘 要 本报告讨论了中国象棋程序设计的关键技术和方法。首先介绍了中国象棋的棋盘制作&#xff0c;利用Qt中的一些绘画类的函数来进行绘制。在创作中国象棋棋子方面&#xff0c;首先&#xff0c;我们先定义一下棋子类&#xff0c;将棋子中相同的部分进行打包&#xff0c;使…

Python:安装/Mac

之前一直陆陆续续有学python&#xff01;今天开始&#xff01;正式开肝&#xff01;&#xff01;&#xff01; 进入网站&#xff1a;可能会有点慢&#xff0c;多开几个网页 https://www.python.org 点击下载&#xff0c;然后进入新的页面&#xff0c;往下滑 来到File&#xff0…

PHP验证日本免费电话号码格式

首先&#xff0c;您需要了解免费电话号码的格式。 日本免费电话也就那么几个号段&#xff1a;0120、0990、0180、0570、0800等开头的&#xff0c;0800稍微特殊点&#xff0c;在手机号里面有080 开头&#xff0c;但是后面不一样了。 关于免费电话号码的划分&#xff0c;全部写…

忘记Apple ID密码怎么退出苹果ID账号?

忘记Apple ID密码怎么退出账号&#xff1f;Apple ID对每个苹果用户来说都是必不可少的&#xff0c;没有它&#xff0c;用户就不能享受iCloud、App Store、iTunes等服务。苹果手机软件下载、丢失解锁、恢复出厂设置等都需要使用Apple ID。如果忘记Apple ID 密码&#xff0c;这会…

Linux——多线程(五)

1.线程池 1.1初期框架 thread.hpp #include<iostream> #include <string> #include <unistd.h> #include <functional> #include <pthread.h>namespace ThreadModule {using func_t std::function<void()>;class Thread{public:void E…

九、Linux二进制安装ElasticSearch集群

目录 九、Linux二进制安装ElasticSearch集群1 下载2 安装前准备(单机&#xff0c;集群每台机器都需要配置)3 ElasticSearch单机&#xff08;7.16.2&#xff09;4 ElasticSearch集群&#xff08;8.14.2&#xff09;4.1 解压文件&#xff08;先将下载文件放到/opt下&#xff09;4…

Java系列-valitile

背景 volatile这个关键字可以说是面试过程中出现频率最高的一个知识点了&#xff0c;面试官的问题也是五花八门&#xff0c;各种刁钻的角度。之前也是简单背过几道八股文&#xff0c;什么可见性&#xff0c;防止指令重拍等&#xff0c;但面试官一句&#xff1a;volatile原理是什…

Vue基础--v-model/v-for/事件属性/侦听器

目录 一 v-model表单元素 1.1 v-model绑定文本域的value 1.1.1 lazy属性&#xff1a;光标离开再发请求 1.1.2 number属性&#xff1a;如果能转成number就会转成numer类型 1.1.3 trim属性&#xff1a;去文本域输入的前后空格 1.2v-model绑定单选checkbox 1.3代码展示 二 …

Python OpenCV 教学取得视频资讯

这篇教学会介绍使用OpenCV&#xff0c;取得影像的长宽尺寸、以及读取影像中某些像素的颜色数值。 因为程式中的OpenCV 会需要使用镜头或GPU&#xff0c;所以请使用本机环境( 参考&#xff1a;使用Python 虚拟环境) 或使用Anaconda Jupyter 进行实作( 参考&#xff1a;使用Anaco…

基于单片机的温湿度感应智能晾衣杆系统设计

&#xff3b;摘 要&#xff3d; 本设计拟开发一种湿度感应智能晾衣杆系统 &#xff0c; 此新型晾衣杆是以单片机为主控芯片 来控制的实时检测系统 &#xff0e; 该系统使用 DHT11 温湿度传感器来检测大气的温湿度 &#xff0c; 然后通过单 片机处理信息来控制 28BYJ &…

配置路由器支持Telnet操作 计网实验

实验要求&#xff1a; 假设某学校的网络管理员第一次在设备机房对路由器进行了初次配置后&#xff0c;他希望以后在办公室或出差时也可以对设备进行远程管理&#xff0c;现要在路由器上做适当配置&#xff0c;使他可以实现这一愿望。 本实验以一台R2624路由器为例&#xff0c;…

使用 Hugging Face 的 Transformers 库加载预训练模型遇到的问题

题意&#xff1a; Size mismatch for embed_out.weight: copying a param with shape torch.Size([0]) from checkpoint - Huggingface PyTorch 这个错误信息 "Size mismatch for embed_out.weight: copying a param with shape torch.Size([0]) from checkpoint - Hugg…

Redis管理禁用命令

在redis数据量比较大时&#xff0c;执行 keys * &#xff0c;fluashdb 这些命令&#xff0c;会导致redis长时间阻塞&#xff0c;大量请求被阻塞&#xff0c;cpu飙升&#xff0c;严重可能导致redis宕机&#xff0c;数据库雪崩。所以一些命令在生产环境禁止使用。 Redis 禁用命令…

开始尝试从0写一个项目--前端(二)

修改请求路径的位置 将后续以及之前的所有请求全都放在同一个文件夹里面 定义axios全局拦截器 为了后端每次请求都需要向后端传递jwt令牌检验 ps&#xff1a;愁死了&#xff0c;翻阅各种资料&#xff0c;可算是搞定了&#xff0c;哭死~~ src\utils\request.js import axio…

【QML之·基础语法概述】

系列文章目录 文章目录 前言一、QML基础语法二、属性三、脚本四、核心元素类型4.1 元素可以分为视觉元素和非视觉元素。4.2 Item4.2.1 几何属性(Geometry&#xff09;:4.2.2 布局处理:4.2.3 键处理&#xff1a;4.2.4 变换4.2.5 视觉4.2.6 状态定义 4.3 Rectangle4.3.1 颜色 4.4…

互联网3.0时代的变革者:华贝甄选大模型创新之道

在当今竞争激烈的商业世界中&#xff0c;华贝甄选犹如一颗璀璨的明星&#xff0c;闪耀着独特的光芒。 华贝甄选始终将技术创新与研发视为发展的核心驱动力。拥有先进的研发团队和一流设施&#xff0c;积极探索人工智能、大数据、区块链等前沿技术&#xff0c;为用户提供高性能…

Knife4j的介绍与使用

目录 一、简单介绍1.1 简介1.2 主要特点和功能&#xff1a; 二、使用步骤&#xff1a;2.1 添加依赖&#xff1a;2.2 yml数据源配置2.3 创建knife4j配置类2.4 注解的作用 最后 一、简单介绍 1.1 简介 Knife4j 是一款基于Swagger的开源文档管理工具&#xff0c;主要用于生成和管…

【PTA天梯赛】L1-003 个位数统计(15分)

作者&#xff1a;指针不指南吗 专栏&#xff1a;算法刷题 &#x1f43e;或许会很慢&#xff0c;但是不可以停下来&#x1f43e; 文章目录 题目题解总结 题目 题目链接 题解 使用string把长度达1000位的数字存起来开一个代表个位数的数组 a[11]倒序计算最后一位&#xff0c;…

第16章 主成分分析:四个案例及课后习题

1.假设 x x x为 m m m 维随机变量&#xff0c;其均值为 μ \mu μ&#xff0c;协方差矩阵为 Σ \Sigma Σ。 考虑由 m m m维随机变量 x x x到 m m m维随机变量 y y y的线性变换 y i α i T x ∑ k 1 m α k i x k , i 1 , 2 , ⋯ , m y _ { i } \alpha _ { i } ^ { T } …

从微软 Word 中提取数据

从 Microsoft Word 文档中提取数据可以通过编程来实现&#xff0c;有几种常见的方法&#xff0c;其中之一是使用 Python 和 python-docx 库。python-docx 是一个处理 .docx 文件&#xff08;Microsoft Word 文档&#xff09;的 Python 库&#xff0c;可以读取和操作 Word 文档的…