POSIX信号量以及利用POSIX信号量实现基于循环队列的高效生产者消费者模型

news2024/9/20 10:45:52
🍑个人主页:Jupiter.
🚀 所属专栏:Linux从入门到进阶
欢迎大家点赞收藏评论😊

在这里插入图片描述

在这里插入图片描述

目录

  • `🍁POSIX信号量 `
    • `🍁信号量的相关接口介绍`
            • *初始化信号量*
            • *销毁信号量*
            • *等待信号量*
            • *发布信号量*
    • `🍁(POSIX信号量):基于环形队列的生产消费模型`
      • `🍁基于环形队列的生产消费模型代码实现`


🍁POSIX信号量

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

🍁信号量的相关接口介绍

初始化信号量

#include <semaphore.h>
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); //P()
发布信号量
  • 功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
    int sem_post(sem_t *sem);//V()

注意:申请成功后,不需要判断资源是否具备条件,因为申请成功后,就一定有对应的资源提供给你,从而可以有效减少内部判断(与条件变量相比,代码上少很多的判断)。


🍁(POSIX信号量):基于环形队列的生产消费模型

上一节生产者-消费者的例子是基于阻塞队列实现的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序

环形队列采用数组模拟,用模运算来模拟环状特性

环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态。

但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程。

🍁基于环形队列的生产消费模型代码实现

实现思想:定义两个下标,记录生产者和消费者的位置,便于在指定下标下生产和执行任务,定义两个信号量,生产者和消费者各一个,记录自己的资源,定义两个锁,生产者消费者各一个,为了让生产者之间与消费者之间实现互斥。

伪代码:
在这里插入图片描述

main.cc

#include <iostream>
#include "thread.hpp"
#include "ringqueue.hpp"
#include <vector>
#include <string>

using namespace std;
using namespace ThreadModule;

void consumer(Ring_Queue<int> &rq)
{
    sleep(1);
    while (true)
    {
        sleep(2);
        int data = 0;
        rq.pop(&data);
        cout << "consumer consume data is:" << data << endl;
    }
}

void product(Ring_Queue<int> &rq)
{
    sleep(1);
    int cnt = 10;
    while (true)
    {
        rq.Enqueue(cnt);  //让数字模拟任务
        cout << "productor product data is:" << cnt++ << endl;
    }
}

void InitConsumer(vector<Thread<Ring_Queue<int>>> *threads, int n, Ring_Queue<int> &rq)
{
    for (int i = 0; i < n; i++)
    {
        string name = "comsumer -" + to_string(i + 1);
        threads->emplace_back(consumer, rq, name);
    }
}

void Initproductor(vector<Thread<Ring_Queue<int>>> *threads, int n, Ring_Queue<int> &rq)
{
    for (int i = 0; i < n; i++)
    {
        string name = "product -" + to_string(i + 1);
        threads->emplace_back(product, rq, name);
    }
}

void StartAll(vector<Thread<Ring_Queue<int>>> &threads)
{
    for (auto &thread : threads)
    {
        thread.start();
    }
}


void waitAllthread(vector<Thread<Ring_Queue<int>>> &threads)
{
    for (auto &thread : threads)
    {
        thread.join();
    }
}

int main()
{
    Ring_Queue<int> *rq = new Ring_Queue<int>(5);
    vector<Thread<Ring_Queue<int>>> threads; 

    InitConsumer(&threads, 3, *rq);  //初始化消费者线程(创建)
    Initproductor(&threads, 1, *rq);  //初始化生产者线程(创建)

    StartAll(threads);  //启动所有的线程
    
    waitAllthread(threads);

    return 0;
}

ringqueue.hpp

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

using namespace std;
template <class T>
class Ring_Queue
{
public:
	//封装PV操作
    void P(sem_t *p)
    {
        sem_wait(p);
    }
    void V(sem_t *v)
    {
        sem_post(v);
    }

public:
    Ring_Queue(int cap) : _ring_queue(cap), _cap(cap), _consumer_dex(0), _product_dex(0)
    {
        sem_init(&_room, 0, _cap);
        sem_init(&_data, 0, 0);

        pthread_mutex_init(&_consumer_mutex, nullptr);
        pthread_mutex_init(&_product_mutex, nullptr);
    }
    void Enqueue(const T &in)
    {
    //申请信号量,如果申请成功,则说明有对应的资源,内部不用判断
        P(&_room);
        //加锁,目的:让生产者之间互斥
        pthread_mutex_lock(&_product_mutex);
        // 生产
        _ring_queue[_product_dex++] = in;
        _product_dex %= _cap;

        pthread_mutex_unlock(&_product_mutex);
        V(&_data);
    }

    void pop(T *out)
    {
        P(&_data);
        //目的:让消费者之间互斥
        pthread_mutex_lock(&_consumer_mutex);

        // 消费
        *out = _ring_queue[_consumer_dex++];
        _consumer_dex %= _cap;

        pthread_mutex_unlock(&_consumer_mutex);
        V(&_room);
    }

    ~Ring_Queue()
    {
        sem_destroy(&_room);
        sem_destroy(&_data);

        pthread_mutex_destroy(&_consumer_mutex);
        pthread_mutex_destroy(&_product_mutex);
    }

private:
    // 1.环形队列
    vector<T> _ring_queue;   //循环队列
    int _cap;     //队列总容量

    int _consumer_dex; //消费者的下标
    int _product_dex;  //生产者的下标

    sem_t _room;  //生产者的信号量
    sem_t _data;  //消费者的信号量

    pthread_mutex_t _consumer_mutex;  //消费者间的锁
    pthread_mutex_t _product_mutex;   //生产者间的锁
};

thread.hpp

#ifndef __THREAD_HPP__
#define __THREAD_HPP__

#include <iostream>
#include <unistd.h>
#include <functional>
#include <string>

namespace ThreadModule
{
    template <class T>
    using func_t = std::function<void(T &)>;

    template <class T>
    class Thread
    {
    public:
        Thread(func_t<T> func, T &data, std::string &name)
            : _func(func), _data(data), _name(name), _stop(true)
        {
        }
        void execute()
        {
            _func(_data);
        }
        static void *threadrun(void *args)
        {
            Thread<T> *self = static_cast<Thread<T> *>(args);
            self->execute();

            return nullptr;
        }
        bool start()
        {
            int n = pthread_create(&_tid, nullptr, threadrun, this);
            if (!n)
            {
                _stop = false;
                return true;
            }
            else
            {
                return false;
            }
        }
        void stop()
        {
            _stop = true;
        }
        void detach()
        {
            if (!_stop)
                pthread_detach(_tid);
        }

        std::string name()
        {
            return _name;
        }

        void join()
        {
            if (!_stop)
                pthread_join(_tid, nullptr);
        }

        ~Thread() {}

    private:
        pthread_t _tid;
        func_t<T> _func;
        T &_data;
        std::string _name;
        bool _stop;
    };
}

#endif


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

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

相关文章

YOLOv9 简介

YOLO v9 是目前表现最佳的目标检测器之一&#xff0c;被视为现有 YOLO 变体&#xff08;如 YOLO v5、YOLOX 和 YOLO v8&#xff09;的改进版本。 YOLOv9 在实时目标检测领域取得了重大进展&#xff0c;引入了诸如可编程梯度信息&#xff08;PGI&#xff09;和通用高效层聚合网…

后端开发刷题 | 打家劫舍

描述 你是一个经验丰富的小偷&#xff0c;准备偷沿街的一排房间&#xff0c;每个房间都存有一定的现金&#xff0c;为了防止被发现&#xff0c;你不能偷相邻的两家&#xff0c;即&#xff0c;如果偷了第一家&#xff0c;就不能再偷第二家&#xff1b;如果偷了第二家&#xff0…

Dina靶机详解

靶机下载 https://www.vulnhub.com/entry/dina-101,200/ 靶机配置 默认是桥接模式&#xff0c;切换为NAT模式后重启靶机 主机发现 arp-scan -l 端口扫描 nmap -sV -A -T4 192.168.229.157 发现80端口开启&#xff0c;访问 访问网站 目录扫描 python dirsearch.py -u http…

1.2 交换技术

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言一、电路交换1. 定义与原理2. 工作过程3. 优点与局限 二、分组交换1. 定义与原理2. 工作过程3. 优点与局限 三、报文交换1. 定义与原理2. 工作过程3. 优点与局限 四、比较…

改进RRT*的路径规划算法

一、RRT算法 RRT 算法是一种基于随机采样的快速搜索算法。该算法的主要思想是通过随机采样来创建一个快速探索的树&#xff0c;从而生长出一条从起点到终点的路径。如图为随机树的生长过程。 初始化。首先&#xff0c;初始化起始点和目标点位置&#xff0c;并将起点作为根节点…

printf()函数的全面介绍及用法——简单易懂

printf&#xff08;&#xff09;函数介绍 目录 printf&#xff08;&#xff09;函数介绍 一&#xff1a;头文件 二&#xff1a;格式控制字符串 1.格式字符。 2.转义字符。 3.普通字符。 三&#xff1a;格式字符输出示例 1. %c-----------输出字符 2. %s-----------输…

Linux中断实操-概念

1、裸机中的中断处理方法&#xff1a; &#xff08;1&#xff09;使能中断、初始化相应寄存器 &#xff08;2&#xff09;注册中断服务函数&#xff0c;向irqTable数组的指定标号处写入中断服务函数 &#xff08;3&#xff09;中断发生后进入IRQ中断服务函数&#xff0c;执行对…

【0~1】实现一个精简版的Tomcat服务器

真正的勇气&#xff0c;是在知道生活的真相之后&#xff0c;依然热爱生活。 《To Kill a Mockingbird》 01 Tomcat 介绍 Tomcat 是一个开源的 Java 应用服务器&#xff0c;主要用来运行基于 Servlet 和 JSP 技术的 Web 应用。Tomcat 实现了 Servlet 规范和 JSP 规范&#xff0…

一次RPC调用过程是怎么样的?

注册中心 RPC&#xff08;Remote Procedure Call&#xff09;翻译成中文就是 {远程过程调用}。RPC 框架起到的作用就是为了实现&#xff0c;调用远程方法时&#xff0c;能够做到和调用本地方法一样&#xff0c;让开发人员更专注于业务开发&#xff0c;不用去考虑网络编程等细节…

【开源免费】基于SpringBoot+Vue.JS企业客户管理系统(JAVA毕业设计)

本文项目编号 T 036 &#xff0c;文末自助获取源码 \color{red}{T036&#xff0c;文末自助获取源码} T036&#xff0c;文末自助获取源码 目录 一、系统介绍1.1 管理员角色1.2 普通员工角色1.3 系统特点 二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内…

苹果手机备份照片怎么删除

在数字时代&#xff0c;备份照片是保护我们珍贵记忆不受意外丢失影响的一种重要方式。苹果手机用户通常利用iCloud或iTunes来备份他们的照片&#xff0c;确保数据的安全。然而&#xff0c;随着时间的推移&#xff0c;这些备份可能会积累大量不再需要的照片&#xff0c;占用宝贵…

鸿蒙开发之ArkTS 基础二

ArkTS常用的基础数据类型 1.字符串 关键字是string 2.数字 关键字是number 3.布尔 关键字是boolean 语法格式是:let 变量名:变量类型 变量值 其中let是关键表示变量&#xff0c;可以修改&#xff0c;可以改变一只对应的是const 修饰&#xff0c;常量不能修改&#xff0c;…

Python画笔案例-050 绘制天空之眼

1、绘制天空之眼 通过 python 的turtle 库绘制 天空之眼&#xff0c;如下图&#xff1a; 2、实现代码 绘制 天空之眼&#xff0c;以下为实现代码&#xff1a; """天空之眼.py """ import math import turtledef draw_square(length,level):if l…

idea同时装了两个版本,每次打开低版本都需要重新激活破解

问题描述&#xff1a; idea同时装了两个版本&#xff0c;每次打开低版本都需要重新激活破解。低版本是2021.1&#xff0c;高版本是2023.1 解决方案&#xff1a; 找到idea的配置路径&#xff0c;比如我的是&#xff1a;C:\Users\Administrator\AppData\Roaming\JetBrains 2021…

【我要成为配环境高手】Nodejs安装与配置

文章目录 1.nodejs安装2.配置npm的全局安装路径3.切换npm的淘宝镜像4.安装vue-cli 1.nodejs安装 从官网下载安装LTS版本的nodejs nodejs会自动安装环境变量&#xff0c;因此安装完成后直接在cmd中查看node版本 node -v2.配置npm的全局安装路径 以管理员身份运行cmd&#xff…

office 2021安装教程

软件介绍 Microsoft Office是微软公司开发的一套基于 Windows 操作系统的办公软件套装。常用组件有 Word、Excel、Powerpoint等。该软件最初出现于九十年代早期&#xff0c;最初是一个推广名称&#xff0c;指一些以前曾单独发售的软件的合集。当时主要的推广重点是购买合集比单…

matlab边缘点提取函数

1、边缘提取 matlab自带点云边缘提取函数,用于搜索点云边界,其核心是alpha shapes算法。alpha shapes提取边缘点,主要是依据滚动圆绕点云进行旋转,实现边缘检测,原理如下图所示。具体原理及效果,可以参考之前我写的博客:基于alpha shapes的边缘点提取(matlab)-CSDN博客…

实习项目|苍穹外卖|day10

Spring Task cron 表达式 入门案例 订单状态定时处理 通知用户支付&#xff01;通知商家完成订单&#xff01; Scheduled(cron "0 0/1 * * * ? ")public void processTimeoutOrder(){log.info("定时处理超时订单: {}", LocalDateTime.now());//答案是…

黑马程序员Java笔记整理(day01)

1.windowsR进入运行&#xff0c;输入cmd 2.环境变量 3.编写java第一步 4.使用idea 5.注释 6.字面量 7.变量 8.二进制 9.数据类型 10.关键词与标识符

仿真软件PROTEUS DESIGN SUITE遇到的一些问题

仿真软件PROTEUS DESIGN SUITE遇到的一些问题 软件网上有很多下载地址自己找哈! 首先如果遇到仿真 没有库 ,需要在网上下载库文件替换到DATA目录下 如果不是默认安装到C盘需要手动修改这些地址,不然会报错!! 当遇到点击仿真出现报错 : 检查这个设置地址是否正确: 随便在库文…