【POSIX信号量】基于环形队列的生产消费模型

news2024/9/23 9:37:34

文章目录

  • POSIX信号量
    • 初始化信号量
    • 销毁信号量
    • 等待信号量(P操作)
    • 发送信号量(V操作)
  • 基于环形队列的生产消费模型
    • 设计思路
    • 代码实现

POSIX信号量

POSIX和System V一样,都是unix下的一套管理方法,下面介绍POSIX标准下的信号量。 POSIX信号量和System
V信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源的目的。除此之外,POSIX信号量可以控制多个线程或者进程对共享资源的访问以达到线程同步的目的。更具体的,信号量是一个资源计数器:当信号量的计数大于0时表示还有资源可以使用,当计数等于0时,表示资源不可以用,需要等待资源。为了便于理解,我们可以认为信号量其实是一个无符号整型,大于0时线程就可以进入临界区访问临界资源并且计数减一,表示可用的资源又少了。直到线程访问资源结束释放资源计数加一。

下面介绍关于操作信号量的函数使用

初始化信号量

#include <semaphore.h>

// 无名信号量初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
// 命名信号量打开
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

对于无名信号量:

  • sem:指向信号量对象的指针
  • pshared:为0表示在线程间共享,否则表示在进程之间共享
  • value:初始化信号量的值

对于命名信号量:

  • name :信号量的名称必须以斜杆/开头
  • oflag:用于指定打开信号量的标志
    • O_CREAT:信号量不在就创建一个
    • O_EXCL:如果同时指定了O_CREAT并且信号量已存在,则打开失败
  • mode:用于设置新创建信号量的权限(类似文件权限)。只有在指定了O_CREAT标志时才使用。权限码和文件的权限码对应
  • value:初始化信号量的值

销毁信号量

// 销毁无名信号量
int sem_destroy(sem_t *sem);

// 关闭命名信号量
int sem_close(sem_t *sem);

// 取消命名信号量链接
int sem_unlink(const char *name);
  • sem表示一个指向信号量对象的指针
  • name表示命名信号名字

等待信号量(P操作)

等待信号量(也称为P操作)会将信号量的值减一,如果信号量的值为零,调用线程将被阻塞,直到信号量的值大于零

int sem_wait(sem_t *sem);
  • sem:指向信号量对象的指针

发送信号量(V操作)

发送信号量(也称为V操作)会将信号量的值加一,如果有任何线程在等待该信号量,它们将被唤醒

int sem_post(sem_t *sem);
  • sem:指向信号量对象的指针

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

之前的生产者消费者模型是基于阻塞队列queue来实现的,其空间可以动态分配,依靠互斥锁和条件变量来实现同步机制。现在基于固定大小的的环形队列使用信号量机制重写这个程序,即实现生产者和消费者的同步访问资源。点击了解环形队列。

同样的,整个模型有1个数据缓冲区即环形队列,2个角色即生产者和消费者,3种关系即生产者和生产者、生产者和消费者、消费者和消费者之间得到关系。

设计思路

下面介绍该模型的设计思路

对于环形队列的基本结构

  • head指针:指向下一个将被消费的位置,即数据出队
  • tail指针:指向下一个被生产的位置,即数据入队

判断队列是否为空和是否为满:
假设整个环形队列要存N个数据,为了方便判断,我们多开一个数据空间不存储数据。如果tail==head,表示初始状态即队列为空。如果 (tail+1)%(N+1)==head 则表示队列为满。为空时,消费者等待,生产者被唤醒。为满时生产者等待,消费者被唤醒

同步机制:
为了保证多线程环境下的安全操作,需要使用到信号量和互斥锁。大致策略为:设置两种信号量,一种表示可用资源数,一种表示空闲空间数。为什么还要设计一个信号量表示空闲空间数呢?这是为了避免生产者和消费者的忙等待。 此外,在PV操作时需要使用互斥锁。

对于忙等待作出具体解释:
如果设计单信号量表示数据个数,生产者生成数据并尝试插入到缓冲区。如果缓冲区已满,生产者将释放互斥锁并继续检查缓冲区状态,直到有空闲空间。这种反复检查状态的行为就是忙等待

代码实现

  • ringqueue.hpp文件,声明定义了循环队列模板。
#pragma once
#include <iostream>
#include <pthread.h>
#include <vector>
#include <semaphore.h>

using namespace std;

template <class T>
class RingQueue
{
private:
    void P(sem_t &s)
    {
        sem_wait(&s);
    }

    void V(sem_t &s)
    {
        sem_post(&s);
    }

public:
    RingQueue(const size_t max_cap) // 构造环形队列
        : _ringqueue(max_cap), _max_cap(max_cap), _c_step(0), _p_step(0)
    {
        sem_init(&_data_sem, 0, 0); // 初始化信号量
        sem_init(&_emp_sem, 0, _max_cap);

        pthread_mutex_init(&_p_mutex, nullptr);
        pthread_mutex_init(&_c_mutex, nullptr);
    }

    void Push(const T &in)
    {                // 生产者
        P(_emp_sem); // 申请空间
        pthread_mutex_lock(&_p_mutex);
        _ringqueue[_p_step] = in;
        _p_step++;
        _p_step = _p_step % _max_cap;
        pthread_mutex_unlock(&_p_mutex);
        V(_data_sem); // 释放数据
    }
    void Front(T *out) // 消费者
    {
        P(_data_sem); // 申请数据
        pthread_mutex_lock(&_c_mutex);
        *out = _ringqueue[_c_step];
        _c_step++;
        _c_step = _c_step % _max_cap;
        pthread_mutex_unlock(&_c_mutex);
        V(_emp_sem); // 释放空间
    }

    ~RingQueue() // 析构销毁资源
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_emp_sem);

        pthread_mutex_destroy(&_p_mutex);
        pthread_mutex_destroy(&_c_mutex);
    }

private:
    vector<T> _ringqueue;
    int _max_cap;

    int _c_step; // 消费者指针的位置
    int _p_step; // 生产者指针的位置

    sem_t _data_sem; // 表示数据个数
    sem_t _emp_sem;  // 表示空闲空间个数

    pthread_mutex_t _p_mutex;
    pthread_mutex_t _c_mutex;
};
  • main.cpp 文件,主函数,定义了多个线程,模拟实现多消费者多生产者访问临界区的情况
#include "RingQueue.hpp"
#include <unistd.h>
using namespace std;

void *Consumer(void *arg)
{
    RingQueue<int> *rq = static_cast<RingQueue<int> *>(arg);
    while (true)
    {
        int data = 0;
        rq->Front(&data);
        cout << "生产者获得数据->" << data << endl;
        sleep(1);
    }
}

void *Produce(void *arg)
{
    RingQueue<int> *rq = static_cast<RingQueue<int> *>(arg);
    while (true)
    {
        int data = rand() % 100;
        rq->Push(data);
        cout << "生产者生产了数据->" << data << endl;
        sleep(1);
    }
}

int main()
{
    pthread_t t1, t2, t3, t4;
    srand(time(nullptr));
    RingQueue<int> *rq = new RingQueue<int>(10);

    pthread_create(&t1, nullptr, Consumer, rq);
    pthread_create(&t2, nullptr, Produce, rq);
    pthread_create(&t3, nullptr, Consumer, rq);
    pthread_create(&t4, nullptr, Produce, rq);

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);
    pthread_join(t4, nullptr);
    return 0;
}
  • 运行结果
    在这里插入图片描述

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

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

相关文章

亚马逊云科技 Amazon Bedrock 构建 AI 应用体验

前言 大模型应用发展迅速&#xff0c;部署一套AI应用的需求也越来越多&#xff0c;从头部署花费时间太长&#xff0c;然而亚马逊科技全托管式生成式 AI 服务 Amazon Bedrock&#xff0c;Amazon Bedrock 简化了从基础模型到生成式AI应用构建的复杂流程&#xff0c;为客户铺设了…

跳水板00

题目链接 跳水板 题目描述 注意点 返回的长度需要从小到大排列必须正好使用k块木板0 < shorter < longer0 < k < 100000 解答思路 用k块两种不同的木板&#xff0c;组合数为k 1&#xff0c;最小的组合为全部使用shorter&#xff0c;每多一块longer&#xff0…

实验04 白盒测试

知识点 白盒测试概述 白盒测试基于程序内部逻辑结构进行测试&#xff0c;关注程序语句、路径、变量状态等。单元测试主要采用白盒测试方法&#xff0c;辅以黑盒测试方法。程序内部结构示意图 白盒测试关注的对象 源代码 检验代码规范性&#xff0c;查找逻辑、内存管理、数…

Jupyter + Pyspark + Yarn 交互式大数据分析

背景: ​ 小批量数据可以使用pandas 进行分析&#xff0c;方便灵活。但大批量&#xff08;千万级别&#xff09;数据&#xff0c;使用pandas分析处理&#xff0c;速度很慢&#xff0c;且需一次性读取全部数据&#xff0c;内存可能溢出。 ​ 此时使用spark分布式分析处理速度很…

记VMware网络适配器里的自定义特定虚拟网络一直加载问题解决办法

1、问题描述 VMware网络适配器里的自定义特定虚拟网络一直加载问题&#xff1a; 在自定义&#xff1a;特定虚拟网络选择的时候 没有上图所示的三个选择&#xff0c;而是正在加载虚拟网络.... 如下图所示&#xff1a; 2、解决办法 2.1、原因分析&#xff1a; 是安装时候出现…

【Java】Idea运行JDK1.8,Build时中文内容GBK UTF-8编码报错一堆方块码

问题描述 在Windows系统本地运行一个JDK1.8的项目时&#xff0c;包管理用的Gradle&#xff0c;一就编码报错&#xff08;所有的中文内容&#xff0c;包括中文注释、中文的String字面量&#xff09;&#xff0c;但程序还是正常运行。具体如下&#xff1a; 解决 1. Idea更改编…

springboot+vue系统开发

链接: https://pan.baidu.com/s/1P1YpHAx9QOBPxjFZ9SAbig 提取码: u6f1

精选力扣,牛客链表面试题

&#x1f48e; 欢迎各位大佬互三&#xff1a;我的主页 1. 反转链表 206.反转链表 思考&#xff1a;如果不开辟额外的空间&#xff0c;只在原来的链表上进行修改的话&#xff0c;该用什么方法呢 只需要从第二个元素开始&#xff0c;依次进行头插就可以了 接着修改一下引用就可…

ROS2 + 科大讯飞 初步实现机器人语音控制

环境配置&#xff1a; 电脑端&#xff1a; ubuntu22.04实体机作为上位机 ROS版本&#xff1a;ros2-humble 实体机器人&#xff1a; STM32 思岚A1激光雷达 科大讯飞语音SDK 讯飞开放平台-以语音交互为核心的人工智能开放平台 实现步骤&#xff1a; 1. 下载和处理科大讯飞语音模…

Linux /etc/profile 详解

概述 Linux是一个多用户的操作系统。每个用户登录系统后&#xff0c;都会有一个专用的运行环境。通常每个用户默认的环境都是相同的&#xff0c;这个默认环境实际上就是一组环境变量的定义。用户可以对自己的运行环境进行定制&#xff0c;其方法就是修改相应的系统环境变量&…

python如何查看类的函数

Python非常方便&#xff0c;它不需要用户查询文档&#xff0c;只需掌握如下两个帮助函数&#xff0c;即可查看Python中的所有函数&#xff08;方法&#xff09;以及它们的用法和功能&#xff1a; dir()&#xff1a;列出指定类或模块包含的全部内容&#xff08;包括函数、方法、…

浅谈串口UART通信原理

文章目录 引言并行和串行波特率UART帧格式 引言 UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff0c;通用异步收发器&#xff09;是一种用于串行通信的硬件设备。它允许两个设备之间进行异步数据传输。是一种通用的串行、异步通信总线。该总线有两条数据…

SpringBoot整合XXL_JOB示例

XXL-JOB 是一个分布式任务调度平台&#xff0c;主要用于管理和执行定时任务。它适用于各种场景&#xff0c;例如定时任务、批处理任务、分布式任务等。XXL-JOB 提供了丰富的功能&#xff0c;使得任务调度变得简单、高效和可靠。以下是 XXL-JOB 的一些主要功能和特点&#xff1a…

Centos系统内磁盘分区

Centos系统内磁盘分区 建议如果有重要数据提前做好备份 以根目录扩容50G为例&#xff1a; 1、卸载/home目录 umount /home 2、删除逻辑卷 y确认即可 lvremove /dev/mapper/centos-home 3、df -h查询一下&#xff0c;/home目录已经不见了 4、向根目录分区追加50G容量 lv…

数据销毁境外间谍情报机关逼迫、威胁贷款学生为其窃取我国家秘密

近年来&#xff0c;随着国际形势的复杂多变&#xff0c;境外间谍情报机关的活动也日益猖獗。他们利用各种手段&#xff0c;包括通过校园贷逼迫、威胁贷款学生为其窃取我国国家秘密&#xff0c;这种行为不仅危害了国家安全&#xff0c;也严重损害了社会的公平正义。那么&#xf…

微信小程序毕业设计-汽车维修项目管理系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

(一)高并发压力测试调优篇——MYSQL数据库的调优

前言 在实际项目开发中&#xff0c;很多业务场景下都需要考虑接口的性能要求&#xff0c;追求高并发、高吞吐量。那么对于此类问题如何入手呢&#xff1f;关注作者&#xff0c;不迷路。本节内容主要介绍在数据库db方面的优化&#xff0c;以mysql数据库为例。 关于db的优化&am…

python库(11):Box库简化字典和对象之间的转换

1Box库简介 Box是一个Python库&#xff0c;它提供了一种将数据封装在字典和列表中的方式&#xff0c;同时提供了一些额外的功能&#xff0c;比如数据验证、默认值设置等。这使得Box库非常适合用于配置管理、数据传输对象&#xff08;DTO&#xff09;的创建&#xff0c;以及任何…

PDF 中图表的解析探究

PDF 中图表的解析探究 0. 引言1. 开源方案探究 0. 引言 一直以来&#xff0c;对文档中的图片和表格处理都非常有挑战性。这篇文章记录一下最近工作上在这块的探究。图表分为图片和表格&#xff0c;这篇文章主要记录了对表格的探究。还有&#xff0c;我个人主要做日本项目&…

[C++]——同步异步日志系统(4)

同步异步日志系统 一、日志等级模块设计二、日志消息类设计 一、日志等级模块设计 定义出日志系统所包含的所有日志等级分别为&#xff1a;&#xff08;7个等级&#xff09; UNKNOW0&#xff0c;未知等级的日志DRBUG &#xff0c;调试等级的日志INFO &#xff0c;提示等级的日…