【操作系统】基于环形队列的生产消费模型

这篇博客的重点在于代码实现,理论部分请看CSDN

一、单生产单消费

1.环形队列的实现

单生产单消费的情况下,我们只需要维护生产者和消费者之间的互斥和同步关系即可

将环形队列封装成一个类:首先给出整体框架,接着会说明每一个类内函数的实现

#pragma once#include <iostream>
#include <vector>
#include <cassert>
#include <semaphore.h>// 环形队列的默认大小
static const int gcap = 5;// 设置成模版类型,队列中可以放任意类型的数据
template <class T>
class RingQueue
{
private:// 封装系统调用sem_waitvoid P(sem_t &sem);// 封装系统调用sem_postvoid V(sem_t &sem);public:RingQueue()~RingQueue()// 生产者向队列里放数据void Push(const T &in);// 消费者从队列中取数据void Pop(T *out);private:std::vector<T> _queue; // 数组模拟环形队列int _cap;              // 环形队列的容量sem_t _spaceSem;       // 生产者 -> 空间资源sem_t _dataSem;        // 消费者 -> 数据资源int _producerStep;     // 生产者的位置(数组下标)int _consumerStep;     // 消费者的位置(数组下标)
};

(1) void P(sem_t &sem);

封装系统调用sem_wait,便于在push和pop中使用

void P(sem_t &sem)
{int n = sem_wait(&sem);assert(n == 0);(void)n;
}

(2) void V(sem_t &sem);

封装系统调用sem_post,便于在push和pop中使用

void V(sem_t &sem)
{int n = sem_post(&sem);assert(n == 0);(void)n;
}

(3) RingQueue()

RingQueue(const int &cap = gcap): _queue(cap), _cap(cap)
{// 初始化信号量int n = sem_init(&_spaceSem, 0, _cap);assert(n == 0);n = sem_init(&_dataSem, 0, 0);assert(n == 0);// 初始化生产者和消费者的位置_productorStep = _consumerStep = 0;
}

(4) ~RingQueue()

~RingQueue()
{sem_destroy(&_spaceSem);sem_destroy(&_dataSem);
}

(5) void Push(const T &in);

void Push(const T &in)
{// 生产者要了一个空间,空间信号量--P(_spaceSem);// 把数据放进队列_queue[_producerStep++] = in;// 维护环状结构_producerStep %= _cap;// 队列新增了数据,数据信号量++V(_dataSem);
}

(6) void Pop(T *out);

    // 消费者从队列中取数据

void Pop(T *out)
{// 消费者拿了一个数据,数据信号量--P(_dataSem);// 把数据拿出队列*out = _queue[_consumerStep++];// 维护环状结构_consumerStep %= _cap;// 队列多出了空间,空间信号量++V(_spaceSem);
}

2.上层调用逻辑

#include "RingQueue.hpp"
#include <pthread.h>
#include <ctime>
#include <cstdlib>
#include <sys/types.h>
#include <unistd.h>void *ProductorRoutine(void *rq)
{RingQueue<int> *ringqueue = static_cast<RingQueue<int> *>(rq);// RingQueue<Task> *ringqueue = static_cast<RingQueue<Task> *>(rq);while (true){// // version1// int data = rand() % 10 + 1;// ringqueue->Push(data);// std::cout << "生产完成,生产的数据是:" << data << std::endl;// sleep(1);// version2// 构建/获取 任务 -- 花费时间int x = rand() % 10;int y = rand() % 5;char op = oper[rand() % oper.size()];Task t(x, y, op, mymath);// 生产任务ringqueue->Push(t);// 输出提示std::cout << "生产者派发了一个任务:" << t.toTaskString() << std::endl;sleep(1);}
}void *ConsumerRoutine(void *rq)
{// RingQueue<int> *ringqueue = static_cast<RingQueue<int> *>(rq);RingQueue<Task> *ringqueue = static_cast<RingQueue<Task> *>(rq);while (true){// // version1// int data;// ringqueue->Pop(&data);// std::cout << "消费完成,消费的数据是:" << data << std::endl;// version2Task t;// 消费任务 -- 花费时间ringqueue->Pop(&t);std::cout << SelName() << ",消费者消费了一个任务:" << t() << std::endl;}
}int main()
{srand((unsigned int)time(nullptr) ^ getpid());RingQueue<int> *rq = new RingQueue<int>();// RingQueue<Task> *rq = new RingQueue<Task>();// 单生产,单消费pthread_t p, c;pthread_create(p + i, nullptr, ProductorRoutine, rq);pthread_create(c + i, nullptr, ConsumerRoutine, rq);pthread_join(p[i], nullptr);pthread_join(c[i], nullptr);delete rq;return 0;
}

二、多生产多消费

1.环形队列的实现

多生产多消费的情况下//...

多生产多消费的环形队列与单生产单消费的不同之处 -> 多了两把锁

#pragma once#include <iostream>
#include <vector>
#include <cassert>
#include <semaphore.h>// 环形队列的默认大小
static const int gcap = 5;// 设置成模版类型,队列中可以放任意类型的数据
template <class T>
class RingQueue
{
private:// 封装系统调用sem_waitvoid P(sem_t &sem);// 封装系统调用sem_postvoid V(sem_t &sem);public:RingQueue()~RingQueue()// 生产者向队列里放数据void Push(const T &in);// 消费者从队列中取数据void Pop(T *out);private:std::vector<T> _queue;   // 数组模拟环形队列int _cap;                // 环形队列的容量sem_t _spaceSem;         // 生产者 -> 空间资源sem_t _dataSem;          // 消费者 -> 数据资源int _producerStep;       // 生产者的位置(数组下标)int _consumerStep;       // 消费者的位置(数组下标)pthread_mutex_t _pmutex; // 生产者pthread_mutex_t _cmutex; // 消费者
};

(1) RingQueue()

    RingQueue(const int &cap = gcap): _queue(cap), _cap(cap){int n = sem_init(&_spaceSem, 0, _cap);assert(n == 0);n = sem_init(&_dataSem, 0, 0);assert(n == 0);_productorStep = _consumerStep = 0;pthread_mutex_init(&_pmutex, nullptr);pthread_mutex_init(&_cmutex, nullptr);}

(2) ~RingQueue()

    ~RingQueue(){sem_destroy(&_spaceSem);sem_destroy(&_dataSem);pthread_mutex_destroy(&_pmutex);pthread_mutex_destroy(&_cmutex);}

(3) void Push(const T &in);

    // 生产者void Push(const T &in){//? 代码有没有优化的可能// 你认为:现加锁,后申请信号量;还是现申请信号量,在加锁?// pthread_mutex_lock(&_pmutex);// 申请到了信号量,意味着我一定能进行正常的生产P(_spaceSem);pthread_mutex_lock(&_pmutex);_queue[_productorStep++] = in;_productorStep %= _cap;pthread_mutex_unlock(&_pmutex);V(_dataSem);// pthread_mutex_unlock(&_pmutex);}

(4) void Pop(T *out);

    // 消费者void Pop(T *out){// 你认为:现加锁,后申请信号量;还是现申请信号量,在加锁?// pthread_mutex_lock(&_cmutex);P(_dataSem);pthread_mutex_lock(&_cmutex);*out = _queue[_consumerStep++];_consumerStep %= _cap;pthread_mutex_unlock(&_cmutex);V(_spaceSem);// pthread_mutex_unlock(&_cmutex);}

2.上层调用逻辑

#include "RingQueue.hpp"
#include "Task.hpp"
#include <pthread.h>
#include <ctime>
#include <cstdlib>
#include <sys/types.h>
#include <unistd.h>std::string SelName()
{char name[128];snprintf(name, sizeof(name), "thread[0x%x]", pthread_self());return name;
}void *ProductorRoutine(void *rq)
{// RingQueue<int> *ringqueue = static_cast<RingQueue<int> *>(rq);RingQueue<Task> *ringqueue = static_cast<RingQueue<Task> *>(rq);while (true){// // version1// int data = rand() % 10 + 1;// ringqueue->Push(data);// std::cout << "生产完成,生产的数据是:" << data << std::endl;// sleep(1);// version2// 构建/获取 任务 -- 花费时间int x = rand() % 10;int y = rand() % 5;char op = oper[rand() % oper.size()];Task t(x, y, op, mymath);// 生产任务ringqueue->Push(t);// 输出提示std::cout << SelName() << ",生产者派发了一个任务:" << t.toTaskString() << std::endl;sleep(1);}
}void *ConsumerRoutine(void *rq)
{// RingQueue<int> *ringqueue = static_cast<RingQueue<int> *>(rq);RingQueue<Task> *ringqueue = static_cast<RingQueue<Task> *>(rq);while (true){// // version1// int data;// ringqueue->Pop(&data);// std::cout << "消费完成,消费的数据是:" << data << std::endl;// version2Task t;// 消费任务 -- 花费时间ringqueue->Pop(&t);std::cout << SelName() << ",消费者消费了一个任务:" << t() << std::endl;}
}int main()
{srand((unsigned int)time(nullptr) ^ getpid());// RingQueue<int> *rq = new RingQueue<int>();RingQueue<Task> *rq = new RingQueue<Task>();// 单生产,单消费pthread_t p[4], c[8];for (int i = 0; i < 4; i++)pthread_create(p + i, nullptr, ProductorRoutine, rq);for (int i = 0; i < 4; i++)pthread_create(c + i, nullptr, ConsumerRoutine, rq);// 多生产,多消费?--> 只要保证,最终进入临界区的是一个生产,一个消费就行!// 多生产,多消费的意义?--> for (int i = 0; i < 4; i++)pthread_join(p[i], nullptr);for (int i = 0; i < 4; i++)pthread_join(c[i], nullptr);delete rq;return 0;
}

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

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

相关文章

【Android】Activity组件通信

文章目录 1.使用Intent传递数据2.使用Bundle传递复杂数据3.startActivityForResult 和 onActivityResult4.使用ViewModel共享数据 在Android中&#xff0c;Activity之间的通信是一个常见且重要的任务。以下是一些常用的方法来实现Activity之间的数据传递和通信&#xff1a; 1.使…

如何在Linux环境中的Qt项目中使用ActiveMQ-CPP

文章目录 代码1&#xff1a;消费者代码2&#xff1a;生成者 之前在Linux下的qt程序中使用activeMQ的时候也是用了很多时间去研究&#xff0c;本来想的是好好记录一下&#xff0c;但是当时顾着写代码。很多细节也不想再去走一遍了。大概写一下怎么使用就行了。注意&#xff1a;一…

Qt QCheckBox、QPushButton和QRadioButton详解

QCheckBox&#xff08;复选框&#xff09; 功能&#xff1a;QCheckBox用于创建一个复选框控件&#xff0c;允许用户从多个选项中选择多个。 属性&#xff1a; checkable&#xff1a;决定复选框是否可以被选中或取消选中。checked&#xff1a;表示复选框当前的选中状态&#…

6、显卡品牌分类介绍:技嘉 - 计算机硬件品牌系列文章

技嘉科技是一家以主板、‌显卡在业界缔造无以撼动的地位的科技公司&#xff0c;‌其核心理念是「‌技术创新、‌质量稳定」‌的高标准。‌技嘉专注于关键技术研发&#xff0c;‌其经营范围涵盖家用、‌商用、‌电竞等多元科技领域。‌通过应用突破性的专利技术&#xff0c;‌技…

自编以e为底的指数函数exp,性能接近标准库函数

算法描述&#xff1a; (1). 先做自变量x的范围检查&#xff0c;对于双精度浮点数&#xff0c;自变量不能超出(-1022ln2, 1024ln2)(-708.39, 709.78)&#xff0c;否则exp(x)会溢出。对于单精度浮点数&#xff0c;自变量不能超出(-126ln2, 128ln2)(-87.33, 88.72). 自己使用此函数…

es安装拼音分词后Kibana出现内存错误

出现错误 今天在安装es的拼音分词器&#xff0c;并重启es容器后&#xff0c;登录Kibana无法使用&#xff0c;查询日志发现如下报错 Waiting until all Elasticsearch nodes are compatible with Kibana before starting saved objects migrations... | typelog timestamp2024…

前端react面试基础知识(II)

这些问题涵盖了 React 的很多核心概念和实际应用场景。下面是针对每个问题的详细回答&#xff1a; 1. **React 项目中&#xff0c;如何动态改变组件的 class 来切换样式?** 可以通过条件判断或者状态&#xff08;state&#xff09;来动态改变组件的 class。例如&#xff0c;使…

Day 42 || 完全背包、518. 零钱兑换 II 、 377. 组合总和 Ⅳ、70. 爬楼梯 (进阶)

完全背包 题目链接&#xff1a;卡码网第52题 思路&#xff1a;和之前01背包一样&#xff0c;但是物品可以无限放置&#xff0c;所以之前二维数组中的背包容量是倒序遍历的&#xff0c;现在可以正序遍历即可重复放入。 import java.util.Scanner; public class Main {public …

数据结构-二叉树中的递归

目录 前言 简单手撕二叉树 二叉树节点的求解 二叉树叶子节点的求解 二叉树高度 二叉树第K层节点的个数 二叉树查找值为X的节点 结束语 前言 在这里说声抱歉&#xff0c;好久没更新数据结构了&#xff0c;二叉树的相关内容还没有更新完&#xff0c;是小编的失职&#xff…

在基于AWS EC2的云端k8s环境中 搭建开发基础设施

中间件下载使用helm,这里部署的都是单机版的 aws-ebs-storageclass.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata:name: aws-ebs-storageclass provisioner: kubernetes.io/aws-ebs parameters:type: gp2 # 选择合适的 EBS 类型&#xff0c;如 gp2、io1…

Apache Calcite - 查询优化之自定义优化规则

RelOptRule简介 为了自定义优化规则&#xff0c;我们需要继承RelOptRule类。org.apache.calcite.plan.RelOptRule 是 Apache Calcite 中的一个抽象类&#xff0c;用于定义优化规则。优化规则是用于匹配查询计划中的特定模式&#xff0c;并将其转换为更优化的形式的逻辑。通过继…

2024网鼎杯青龙组wp:Crypto1

题目 附件内容如下 from Crypto.Util.number import * from secret import flag from Cryptodome.PublicKey import RSAp getPrime(512) q getPrime(512) n p * q d getPrime(299) e inverse(d,(p-1)*(q-1)) m bytes_to_long(flag) c pow(m,e,n) hint1 p >> (51…

Python 单元测试中的 Mocking 与 Stubbing:提高测试效率的关键技术

在软件开发过程中&#xff0c;单元测试是确保代码质量的重要环节。为了实现高效的单元测试&#xff0c;我们常常需要隔离待测试的代码与其外部依赖。这时候&#xff0c;Mocking&#xff08;模拟&#xff09;和 Stubbing&#xff08;桩&#xff09;技术就显得尤为重要。这两种技…

Golang | Leetcode Golang题解之第528题按权重随机选择

题目&#xff1a; 题解&#xff1a; type Solution struct {pre []int }func Constructor(w []int) Solution {for i : 1; i < len(w); i {w[i] w[i-1]}return Solution{w} }func (s *Solution) PickIndex() int {x : rand.Intn(s.pre[len(s.pre)-1]) 1return sort.Searc…

3D打印机 屏幕的固定挂钩断后的一次自己修复经历

引子 3D打印机的屏幕固定挂钩断了 这次确实不知道咋断的&#xff0c;这可咋办呢&#xff0c;到网上看了一下&#xff0c;一个屏幕要2佰多&#xff0c;有些小贵&#xff0c;要不就自己修修吧&#xff0c;打个挂钩按上&#xff0c;说干就干。 正文 freecad的设计图如下【其中各…

PHP合成图片,生成海报图,poster-editor使用说明

之前写过一篇使用Grafika插件生成海报图的文章&#xff0c;但是当我再次使用时&#xff0c;却发生了错误&#xff0c;回看Grafika文档&#xff0c;发现很久没更新了&#xff0c;不兼容新版的GD&#xff0c;所以改用了intervention/image插件来生成海报图。 但是后来需要对海报…

Java基于微信小程序的美食推荐系统(附源码,文档)

博主介绍&#xff1a;✌程序猿徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Linux的IP网路命令: 用于显示和操作网络接口(网络设备)的命令ip link详解

目录 一、概述 二、用法 1、基本语法 2、常用选项 3、常用参数 4、获取帮助 三、示例 1. 显示所有网络接口的信息 &#xff08;1&#xff09;命令 &#xff08;2&#xff09;输出示例 &#xff08;3&#xff09;实际操作 2. 启动网络接口 3. 停止网络接口 4. 更改…

【驱动】地平线X3交叉编译工具搭建、源码下载

1、交叉编译工具搭建 1)安装依赖包 sudo apt-get install -y build-essential make cmake libpcre3 libpcre3-dev bc bison \ flex python3-numpy mtd-utils zlib1g-dev debootstrap \ libdata-hexdumper-perl libncurses5-dev zip qemu-user-static \ curl repo git liblz4…

服务器上清理Docker容器运行日志的正确方法

为啥要清理服务器上docker容器的日志 因为是服务器的磁盘空间资源有限&#xff0c;由于docker容器在启动的时候没有限制&#xff0c;导致运行的docker容器随着时间的推移产生的日志越来越多&#xff0c;最后把服务磁盘资源耗尽&#xff0c;服务器的磁盘满了会导致服务器的应用无…