Linux信号量环形队列处理生产消费者模型

news2024/10/10 16:23:48

目录

一、信号量

1.1 信号量的概念

1.2 信号量的函数接口

1.2.1 sem_init()

1.2.2 sem_destroy()

1.2.3 sem_wait()(P操作申请信号量)

1.2.4 sem_post()(V操作增加信号量)

二、环形队列

2.1 环形队列概念

2.2 环形队列操作

三、模拟实现

3.1 环形队列在生产消费模型作用

3.2 信号量P&V操作在生产消费模型中作用

3.3 多消费者多生产者需要几把锁?

3.4 CircularQueue.hpp

3.5 Main.cc


一、信号量

1.1 信号量的概念

信号量是一个用于进程间同步和互斥的机制。它是一个整数值,用于表示特定资源的可用性。简单理解,信号量是一种计数器,它的数值代表着资源的个数,取资源信号量--,称之为P操作,放资源信号量++,简称为V操作。

举个生活中的例子,信号量相当于电影院的电影票,资源是座位。通过申请信号量(购买电影票)提前预定资源(座位)。


1.2 信号量的函数接口

头文件:<semaphore.h>

1.2.1 sem_init()

用于初始化信号量变量。函数原型为:int sem_init(sem_t *sem, int pshared, unsigned int value)。此函数将未命名的信号量初始化为sem指向的地址,参数value指定了初始值(资源的多少)。其中,pshared参数指示信号量是在进程间共享(pshared非零)还是在线程间共享(pshared为0)。

1.2.2 sem_destroy()

用于销毁(释放)信号量变量。函数原型为:int sem_destroy(sem_t *sem)。此函数会销毁sem指向的地址处的未命名信号量。

1.2.3 sem_wait()(P操作申请信号量)

用于“P”操作,即等待信号量值大于0。函数原型为:int sem_wait(sem_t *sem)。没有申请成功会阻塞等待

1.2.4 sem_post()(V操作增加信号量)

函数原型为:int sem_post(sem_t *sem);。此函数会增加信号量sem的值。


二、环形队列

2.1 环形队列概念

环形队列可以是逻辑上是首尾相连队列,实际是一段数组空间!step通过%=数组长度实现环形。


2.2 环形队列操作

环形队列的创建和使用包括以下步骤:

  1. 创建一个定长的数组,作为环形队列的存储空间。
  2. 定义两个指针,一个指向队列的首部(front),另一个指向队列的尾部(rear)。
  3. 当需要入队(加入元素到队列)时,将元素添加到 rear 指针指向的位置,并将 rear 指针向队尾方向移动一位;当 rear 指针到达队列末尾时,将其重置为 0。
  4. 当需要出队(从队列中移除元素)时,将 front 指针指向的元素移除,并将 front 指针向队尾方向移动一位;当 front 指针到达队列末尾时,将其重置为 0。

三、模拟实现

3.1 环形队列在生产消费模型作用

环形队列中存放资源,2种角色生产者消费者在环形队列中移动。队列的长度是信号量的数组大小。生产者放数据,消费者拿数据!两者只有在最开始或者生产者生产数据过快放满数据,两者才会在同一个位置!其他任何情况,两者不在同一位置,各自执行自己的任务!通过step%=数组长度实现循环!


3.2 信号量P&V操作在生产消费模型中作用

因为信号量操作的原子性,当申请信号量成功--》生产者有空间放数据 & 消费者有数据可拿 

否则,二者就要阻塞等待。这样就可以先让两种角色申请信号量,申请成功后在互斥进行具体处理

操作!这样不需要条件变量同步有无资源,因为信号量申请成功就代表了已经获取了资源!这样可

缩短临界区的线度,提高了线程的执行效率

还要考虑一个问题,对于消费者,生产者两者都要有一个信号量,生产者的初始信号量是空余空间的大小!消费者的信号量初始是0,代表数据的多少。两者首先对自己的信号量进行P操作申请,然后处理自己的任务后,完成后同步对方的信号量进行V操作。


3.3 多消费者多生产者需要几把锁?

前面的生产者消费者在环形队列下有两个指针,两者不在同一位置情况下,互补干扰!那么多生产者相互竞争一把锁,抢到锁的生产者在自己的位置放数据;同样,消费者一把锁,抢到锁的消费者拿数据,然后再处理自己位置的数据。也就是说生产者们一把锁,消费者们一把锁


3.4 CircularQueue.hpp

#include <iostream>
#include <vector>
#include <semaphore.h>
#include <assert.h>
template <class T>
class CircularQueue
{
public:
    static const int gcap = 10;
    CircularQueue(const int cap = gcap) : _cap(cap)
    {
        sem_init(&_spaceSem, 0, _cap); // 生产者初始空间容量val=_cap
        sem_init(&_dataSem, 0, 0);     // 消费者初始空间容量 val = 0
        pthread_mutex_init(&_pmutex, nullptr);
        pthread_mutex_init(&_cmutex, nullptr);
        _queue.resize(_cap);
    }

    void push(const T &in)
    {
        P(_spaceSem);
        // 往后说明有位置放数据
        pthread_mutex_lock(&_pmutex);
        _queue[_pstep++] = in;
        _pstep %= _cap;
        pthread_mutex_unlock(&_pmutex);
        //同步消费者
        V(_dataSem);
    }

    void pop(T *out)
    {
        P(_dataSem);
        //往后说明有数据处理
        pthread_mutex_lock(&_cmutex);
        *out = _queue[_cstep++];
        _cstep %= _cap;
        pthread_mutex_unlock(&_cmutex);
        //同步生产者
        V(_spaceSem);
    }
    ~CircularQueue()
    {
        sem_destroy(&_spaceSem);
        sem_destroy(&_dataSem);
        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }

private:
    //sem_wait封装成P操作
    void P(sem_t &sem)
    {
        int n = sem_wait(&sem);
        assert(n == 0);
        (void)n;
    }
    //sem_post封装成V操作
    void V(sem_t &sem)
    {
        int n = sem_post(&sem);
        assert(n == 0);
        (void)n;
    }

private:
    std::vector<T> _queue;//逻辑上环形队列,物理上数组
    int _cap;
    sem_t _spaceSem; // 生产者看重空间资源
    sem_t _dataSem;  // 消费中看重消费资源
    int _pstep = 0;
    int _cstep = 0;
    //生产者们&消费者们各自一把锁
    pthread_mutex_t _pmutex;
    pthread_mutex_t _cmutex;
};

3.5 Main.cc

#include"CircularQueue.hpp"
#include<unistd.h>
#include<sys/types.h>
#include<pthread.h>

std::string GetName()
{
    char name[64];
    snprintf(name,sizeof(name),"thread[0x%x]",pthread_self());
    return name;
}

//生产者任务就是放入一个整数
void* Producer(void* args)
{
    CircularQueue<int>* cq =static_cast<CircularQueue<int>*>(args);
    while(true)
    {
        int task = rand() % 5 + 1;
        cq->push(task);
        std::cout <<GetName()<<" 生产任务: " <<task<<std::endl;
        sleep(1);
    }
}

//消费者不做数据处理,只是拿到数据后打印
void* Consumer(void* args)
{
    CircularQueue<int>* cq =static_cast<CircularQueue<int>*>(args);
    while(true)
    {
        int ret = 0;
        cq->pop(&ret);
        std::cout <<GetName()<<" 消费任务: " << ret <<std::endl; 
        sleep(1);
    }
}

int main()
{
    srand((unsigned int)time(nullptr) ^ getpid());
    CircularQueue<int>* cq = new CircularQueue<int>();
    //多生产者,多消费者
    pthread_t c[4],p[8];
    for(int i =0;i<4;i++)
        pthread_create(c+i,nullptr,Producer,cq);
    for(int i =0;i<8;i++)
        pthread_create(p+i,nullptr,Consumer,cq);
    for(int i=0;i<4;i++)
        pthread_join(c[i],nullptr);
    for(int i =0;i<8;i++)
        pthread_join(p[i],nullptr);
    delete cq;
    return 0;
}

代码结果展示: 

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

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

相关文章

【爬虫】8.1. 深度使用tesseract-OCR技术识别图形验证码

深度使用tesseract-OCR技术识别图形验证码 文章目录 深度使用tesseract-OCR技术识别图形验证码1. OCR技术2. 准备工作3. 简单作用了解3.1. 验证码图片爬取-screenshot_as_png3.2. 识别测试-image_to_string3.2.1. 正确识别3.2.2. 错误识别3.2.3. 灰度调节 3.3. 识别实战-使用im…

qsort 函数的使用

一、qsort 函数的形式 1.1使用 qsort 函数包含的库 1.2qsort 函数的参数 qsort&#xff1a;对数组的元素进行排序 1.3参数中的 compar 函数 struct stu {char name[20];//姓名int age;//年龄double grade;//成绩 }; int cmp_name(void* p1, void* p2) {//如果按照姓名排序ret…

个人信息去标识化具体实施指南

声明 本文是学习个人信息去标识化指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 范围 本标准描述了个人信息去标识化的目标和原则&#xff0c;提出了去标识化过程和管理措施。 本标准针对微数据提供具体的个人信息去标识化指导&#xff0c;适…

windows 查询正在运行的系统

查询当前哪些服务在运行 net start 下图为查询到的正在运行的服务 查看某个服务是否正在运行 方式1 net start |find "MySQL" 出现下图说明mysql服务正在运行 方式2 sc query mysql 出现下图说明服务正在运行

打字侠:一款专业的中文打字网站

打字侠第一个正式版发布啦&#xff01;&#xff01;&#xff01; 虽然离期望的样子还有一段路要走&#xff0c;不过能看到它正式发布&#xff0c;我还是很激动哟&#xff01; 打字侠是一款面向中学生和大学生的在线打字软件&#xff0c;它通过合理的课程设计和精美的图形界面帮…

删除linux(centos7)系统自带的open jdk,安装配置jdk环境

查看jdk版本 安装的linux自带jdk8版本&#xff0c;我们不用自带的。 安装jdk步骤 1、下载 下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads 2、创建目录 创建文件夹&#xff0c;用来部署JDK&#xff0c;将JDK安装部署到&#xff1a;/export/se…

【技术支持案例】S32K146的hard fault问题处理

文章目录 1. 案例背景2. 方案准备2.1 HardFault&#xff08;硬件错误异常&#xff09;2.2 UsageFault&#xff08;用法错误异常&#xff09;2.3 BusFault&#xff08;总线错误异常&#xff09;2.4 MemManage Fault&#xff08;存储器管理错误异常&#xff09; 3. 现场支持3.1 现…

OpenCV项目开发实战--实现面部情绪识别对情绪进行识别和分类及详细讲解及完整代码实现

文末提供免费的完整代码下载链接 面部情绪识别(FER)是指根据面部表情对人类情绪进行识别和分类的过程。通过分析面部特征和模式,机器可以对一个人的情绪状态做出有根据的猜测。面部识别的这个子领域是高度跨学科的,借鉴了计算机视觉、机器学习和心理学的见解。 在这篇研究…

C++数据结构 -- AVL树

目录 一、什么是AVL树&#xff1f;AVL树的概念 二、 AVL树的节点的定义三、 AVL树新结点的插入3.1 左单旋左单旋代码实现3.2 右单旋右单旋代码实现3.3 左单旋或者右单旋解决不了的问题3.4 左右双旋左右双旋代码实现3.5 右左双旋右左双旋代码实现 四、代码汇总 一、什么是AVL树&…

【Vue篇】Vue 项目下载、介绍(详细版)

如何创建一个vue项目&#xff1f;首先要有环境&#xff0c;如下&#xff1a; nodejs vue-cli如果有以上的工具就直接跳过安装教程 【Vue篇】mac上Vue 开发环境搭建、运行Vue项目&#xff08;保姆级&#xff09; 创建vue项目 选择一个位置&#xff0c;你要存放项目的路径&…

App线上网络问题优化策略

在我们App开发过程中&#xff0c;网络是必不可少的&#xff0c;几乎很难想到有哪些app是不需要网络传输的&#xff0c;所以网络问题一般都是线下难以复现&#xff0c;一旦到了用户手里就会碰到很多疑难杂症&#xff0c;所以对于网络的监控是必不可少的&#xff0c;针对用户常见…

golang flag 包的使用指北

说起 golang 的 flag 个包&#xff0c;我们第一反应的是什么呢&#xff1f;至少我曾经第一次看到 flag 包的时候&#xff0c;第一反应是想起写 C 语言的时候咱们用于定义一个表示的&#xff0c;我们一般会命名为 flag 变量 实际上 golang 的 flag 包是用于处理命令行参数的工具…

《深入浅出OCR》第六章:OCR数据集与评价指标

一、OCR技术流程 在介绍OCR数据集开始&#xff0c;我将带领大家和回顾下OCR技术流程&#xff0c;典型的OCR技术pipline如下图所示&#xff0c;其中&#xff0c;文本检测和识别是OCR技术的两个重要核心技术。 1.1 图像预处理&#xff1a; 图像预处理是OCR流程的第一步&#xf…

5147. 数量

题目&#xff1a; 样例1&#xff1a; 输入 4 输出 1 样例2&#xff1a; 输入 7 输出 2 样例3&#xff1a; 输入 77 输出 6 思路&#xff1a; 根据题意&#xff0c;如果直接 for 循环暴力&#xff0c;肯定会超时&#xff0c;但是我们换个思路想&#xff0c;只要包含 4 和 7的…

【2023年数学建模国赛】C题代码与技术文档分享

2023年数学建模国赛C题 第一问代码code1_Q1_1.mCode1_Q1_2.mCode1_Q1_3.m实验结果 技术文档问题分析假设符号说明1 第一问1.1分布检验模型的建立1.2 相关性模型的建立1.3各种类蔬菜的销量分布及相关关系 写在最后 第一问代码 code1_Q1_1.m clc clear Dxlsread(合成表1,合成表…

通过实例学习:使用Spring Cache实现实际场景的缓存策略

文章目录 前言一、Spring Cache 常用注解1.Cacheable&#xff1a;2.CachePut&#xff1a;3.CacheEvict&#xff1a;4.CacheConfig&#xff1a;5.EnableCathing: 二、使用步骤1.引入依赖2.配置3.EnableCaching的使用&#xff1a;4.Cacheable的使用&#xff1a;5.CachePut的使用&…

c语言练习46:模拟实现strncpy

模拟实现strncpy 模拟实现&#xff1a; #include<stdio.h> char* my_strncpy(char*dest,char*src,size_t num) {char* ret dest;size_t i 0;for (i 0; i < num; i) {*dest *src;dest;src;}*dest \0;return ret; } int main() {char aim[50] { 0 };char src[] …

03_kafka-eagle 监控

文章目录 安装修改 kafka-server-start.sh修改 kafka-run-class.sh问题eagle 日志报错mysql 报错 时区问题 kafka-eagle 监控 安装 download.kafka-eagle.org &#xff1a; https://github.com/smartloli/kafka-eagle-bin/archive/v3.0.1.tar.gzhttps://docs.kafka-eagle.org/…

C语言“牵手”lazada商品详情数据方法,lazada商品详情API接口,lazadaAPI申请指南

lazada是东南亚最大的自营式电商企业&#xff0c;在线销售计算机、手机及其它数码产品、家电、汽车配件、服装与鞋类、奢侈品、家居与家庭用品、化妆品与其它个人护理用品、食品与营养品、书籍与其它媒体产品、母婴用品与玩具、体育与健身器材以及虚拟商品等。 lazada平台的商…

C基础-数组

1.一维数组的创建和初始化 int main() {// int arr1[10];int n 0;scanf("%d",&n);//int count 10;int arr2[n]; //局部的变量&#xff0c;这些局部的变量或者数组是存放在栈区的&#xff0c;存放在栈区上的数组&#xff0c;如果不初始化的话&#xff0c;默认…