目录

  • 1. 环形队列的概念与实现方法
    • 1.1 环形队列的概念
    • 1.2 环形队列的一般实现方法
  • 2. 多线程相关的信号量概念与接口
    • 2.1 信号量类型
    • 2.2 信号量的初始化与销毁
    • 2.3 信号量的P申请、V释放操作
  • 3. 基于环形队列实现p、c模型的设计方案
    • 3.1 环形队列(ringqueue)作为共享资源缓冲区的设计要求
    • 3.2 环形队列作为共享资源缓冲区的实现方法
  • 4. 代码实现
    • 4.1 线程类
    • 4.2 线程执行的任务
    • 4.3 RingQueue类
    • 4.4 主干代码

1. 环形队列的概念与实现方法

1.1 环形队列的概念

  • 环形队列:
      容量固定,首尾相连的一个队列(遵循先进先出),于逻辑上形状为一个环形。当存储数据超过容量上限后,再次给环形队列插入元素时,不会产生越界的情况。新的数据会回绕到数据首部进行插入,将空间中的旧数据覆盖
    在这里插入图片描述

1.2 环形队列的一般实现方法

  最常用于实现环形队列的数据结构为顺序表,其通过对下标的取模操作,就可以很好的实现存储空间逻辑上的环形要求,具体实现方案如下:


方案1:

  1. 开辟要求容量cap大小的顺序表

  2. 以变量begin记录队列中首个数据的下标,起始设置为0

  3. 以变量end记录队列中新数据插入位置的下标,起始设置为0
    在这里插入图片描述

  4. 以变量count记录队列中存储数据的个数,每次插入数据后进行++,每次删除数据后进行--。队列为空与为满时,beginend的位置相同,因此,需要以count中记录的值来做区分

  5. 插入元素时,向end记录的下标位置写入数据,然后再++

  6. 删除元素时,只需让begin进行++即可,beginend下标之间的空间中存储着队列中的数据

  7. beginend每次++后都要对其进行取模操作%= cap

方案2:
  方案2的大体实现思路与方案1相同,只是于队列空满判定实现上有所区别。此种方法,会将队列额外多开辟出一块空间,队列容量为cap,实际大小为cap + 1。此块多出来的空间不用来存放数据,而是用来标识环形队列的满状态(end + 1) % (cap + 1) == begin
在这里插入图片描述

2. 多线程相关的信号量概念与接口

2.1 信号量类型

#include <semaphore.h>
//原生线程库中的内容,编译时带-lpthread选项
sem_t sem;//同样为计数器

2.2 信号量的初始化与销毁

信号量的初始化:

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 参数1sem_t* 传入需要初始化的信号量地址
  • 参数2int pshared 用于父子进程之间共享设置为1,用于多线程之间共享设置为0
  • 参数3unsigned int value 用于设置信号量的大小
  • 返回值int 成功时,返回0;失败时,返回-1,并将错误码写入errno

信号量的销毁:

int sem_destroy(sem_t* sem);
  • 参数sem_t* sem 传入需要销毁的信号量的地址
  • 返回值int 成功时,返回0;失败时,返回-1,并将错误码写入errno

2.3 信号量的P申请、V释放操作

信号量的P操作:

int sem_wait(sem_t* sem);
  • 参数sem_t* sem 传入需要进行P操作的信号量地址
  • 返回值int 成功时,返回0;失败时,返回-1,并将错误码写入errno

信号量的V操作:

int sem_post(sem_t* sem);
  • 参数sem_t* sem 传入需要进行V操作的信号量地址
  • 返回值int 成功时,返回0;失败时,返回-1,并将错误码写入errno

  信号量不仅仅其申请与释放操作都是互斥的,而且本身还同时具有计数的作用。因此,在功能上,其就相当于是互斥锁与条件变量的结合,但其又只需要一条操作语句就可以实现互斥锁与条件变量的配合效果。所以,在代码编写角度,信号量比互斥锁 + 条件变量更简单也更简洁。

3. 基于环形队列实现p、c模型的设计方案

3.1 环形队列(ringqueue)作为共享资源缓冲区的设计要求

在这里插入图片描述
  生产者生产数据存入队列,消费者消费数据从队列中获取,此处使用方案1中的环形队列设计方式。

  • 生产者与消费者访问共享资源时,需要保持互斥且同步
  • 环形队列中有多块空间,只有队列为满或为空时,生产者与消费者才会访问到同一块空间,即访问共享资源。因此,除开队列为空为满的情况之外,其他场景中,消费者与生产者都可以并发地访问环形队列

3.2 环形队列作为共享资源缓冲区的实现方法

在这里插入图片描述

  • 对于生产者而言,其需要向队列中插入数据,所以,队列中空余的空间(room)是其所需的资源
  • 对于消费者而言,其需要从队列中获取数据,所以,队列空间中存储的数据(data)是其所需的资源

在这里插入图片描述
  定义两个信号量room_semdata_sem,分别作为生产者资源与消费者资源的计数器,每次在生产者、消费者线程访问环形队列之前,都要预先申请对应的信号量资源。变量productor_index记录生产者生产资源的放置位置,变量consumer_index记录消费者从环形队列中获取数据的位置。生产者之间、消费者之间它们对于环形队列的访问需要保持互斥性,此处实现可以使用一把锁来实现,但这样生产者、消费者之间大多数情况下的并发就会被影响,因此,我们定义分别定义两把锁,来分别实现同类之间的互斥,彼此之间并发。


  • 环形队列作为共享资源缓冲区的优势
      队列中拥有多块空间,生产者与消费者线程大多数情况下都不会访问同一块资源,因此,可以保证它们之间的并发访问,提高程序的效率。

4. 代码实现

4.1 线程类

namespace ThreadModule
{template<typename T>using func_t = function<void(T&, string)>;template<typename T>class Thread{public:Thread(func_t<T> func, T& data, string name = "none-thread"):_func(func), _data(data), _name(name), _stop(true){}~Thread(){}void Execute(){_func(_data, _name);}static void* threadroutine(void* arg){Thread<T>* ptd = static_cast<Thread<T>*>(arg);ptd->Execute();return nullptr;}bool start(){int n = pthread_create(&_tid, nullptr, threadroutine, this);if(n){return false; }_stop = false;return true;}void join(){if(!_stop){pthread_join(_tid, nullptr);}}void detach(){if(!_stop){pthread_detach(_tid);}}string name(){return _name;}void stop(){_stop = true;}private:pthread_t _tid;string _name;func_t<T> _func;T& _data;bool _stop;};}

4.2 线程执行的任务

using task_t = function<void()>;void download()
{cout << "task is download" << endl;
}

4.3 RingQueue类

#ifndef RING_QUEUE
#define RING_QUEUE
#include <vector>
#include <semaphore.h>
#include <pthread.h>template<typename T>
class RingQueue
{
private:void P(sem_t* sem){sem_wait(sem);//wait申请}void V(sem_t* sem){sem_post(sem);//post单词译为放入,post操作为释放}void Lock(pthread_mutex_t* lock){pthread_mutex_lock(lock);}void Unlock(pthread_mutex_t* lock){pthread_mutex_unlock(lock);}public:RingQueue(int cap):_cap(cap), _ringbuffer(cap), _productor_index(0), _consumer_index(0){sem_init(&_room_sem, 0, _cap);//父子进程之间共享pshared: 1, 多线程之间共享pshared: 0sem_init(&_data_sem, 0, 0);pthread_mutex_init(&_productor_lock, nullptr);pthread_mutex_init(&_consumer_lock, nullptr);}void Enqueue(const T& data){P(&_room_sem);Lock(&_productor_lock);_ringbuffer[_productor_index++] = data;_productor_index %= _cap;Unlock(&_productor_lock);V(&_data_sem);}void Pop(T* data){P(&_data_sem);Lock(&_consumer_lock);*data = _ringbuffer[_consumer_index++];_consumer_index %= _cap;Unlock(&_consumer_lock);V(&_room_sem);}~RingQueue(){sem_destroy(&_room_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_productor_lock);pthread_mutex_destroy(&_consumer_lock);}private:vector<T> _ringbuffer;int _cap;int _productor_index;int _consumer_index;sem_t _room_sem;sem_t _data_sem;pthread_mutex_t _productor_lock;pthread_mutex_t _consumer_lock;
};#endif

4.4 主干代码

在这里插入图片描述

using ringqueue_t = RingQueue<task_t>;void ProductorRun(ringqueue_t& rq, string name)
{while(true){rq.Enqueue(download);cout << name << " is producted" << endl;}
}void ConsumerRun(ringqueue_t& rq, string name)
{while(true){sleep(1);task_t task;rq.Pop(&task);cout << name << " get : "; task();//bad_function_call: 尝试调用没有目标的function对象时会出现此异常}
}void InitComm(vector<Thread<ringqueue_t>>& threads, ringqueue_t& rq, int num, func_t<ringqueue_t> func, string who)
{for(int i = 0; i < num; i++){string name = who + '-' + to_string(i + 1);threads.emplace_back(func, rq, name);}
}void InitProductor(vector<Thread<ringqueue_t>>& threads, ringqueue_t& rq, int num)
{InitComm(threads, rq, num, ProductorRun, "productor");
}void InitConsumer(vector<Thread<ringqueue_t>>& threads, ringqueue_t& rq, int num)
{InitComm(threads, rq, num, ConsumerRun, "consumer");
}void StartAll(vector<Thread<ringqueue_t>>& threads)//vector必须用引用,存储线程tid
{for(auto& thread : threads){thread.start();}
}void WaitAllThread(vector<Thread<ringqueue_t>> threads)//尝试用拷贝对象是否能够正常回收进程
{for(auto thread : threads){thread.join();}
}int main()
{ringqueue_t rq(5);vector<Thread<ringqueue_t>> threads;InitProductor(threads, rq, 3);InitConsumer(threads, rq, 2);StartAll(threads);WaitAllThread(threads);return 0;
}

  此处设计时,将创建线程类与真正创建并启动线程分开执行。这是因为直接以threads.back().start()创建后调用启动线程,其中threads.back()迭代器可能会由于不断向threads中插入新的线程类对象,导致发生扩容,最终使得原迭代器失效

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

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

相关文章

【左程云算法07】队列和栈-链表数组实现

目录 ​编辑1&#xff09;队列的介绍 核心操作 3&#xff09;队列的链表实现和数组实现 使用数组实现队列 2&#xff09;栈的介绍 核心操作 4&#xff09;栈的数组实现 使用语言内置的实现 使用数组手动实现栈 5&#xff09;环形队列的实现 leecode622 代码解析 视频…

Docker 清理完整指南:释放磁盘空间的最佳实践

前言 随着 Docker 使用时间的增长,系统中会积累大量的容器、镜像、数据卷和构建缓存,占用大量磁盘空间。本文将详细介绍如何有效清理 Docker 资源,释放磁盘空间,保持系统整洁。 Docker 资源类型 Docker 主要占用磁盘空间的资源包括: 容器 (Containers):运行中和已停止…

零基础快速了解掌握Linux防火墙-Iptables

一、 Iptables概述Iptables 是一个用户空间程序&#xff0c;可以用于设置和管理 Linux 操作系统的内核级防火墙。它通过表、链和 规则组成&#xff0c;可以灵活地根据不同的需求进行配置。iptables 具有以下特点&#xff1a;Iptables 作为内核级别的防火墙&#xff0c;具有高效…

12公里无人机图传模组:从模糊到超高清的飞跃,抗干扰能力全面升级

在无人机行业飞速发展的今天&#xff0c;高清图像传输已成为衡量无人机性能的重要标志之一。过去&#xff0c;无人机在长距离飞行时常常面临信号衰减、图像模糊&#xff0c;甚至数据丢失的问题&#xff0c;影响了用户的体验与应用效果。为了打破这一瓶颈&#xff0c;业内专家不…

从 “模板” 到 “场景”,用 C++ 磨透拓扑排序的实战逻辑

文章目录前言&#xff1a;《算法磨剑: 用C思考的艺术》 专栏《C&#xff1a;从代码到机器》 专栏《Linux系统探幽&#xff1a;从入门到内核》 专栏正文&#xff1a;[B3644 【模板】拓扑排序 / 家谱树](https://www.luogu.com.cn/problem/B3644)【解法】【参考代码】[P2712 摄像…

盲盒抽卡机小程序:从0到1的蜕变之路

盲盒抽卡机小程序从概念提出到最终上线&#xff0c;经历了从0到1的蜕变过程。这个过程充满了挑战与机遇&#xff0c;也凝聚了开发团队的智慧和汗水。本文将分享盲盒抽卡机小程序的开发历程&#xff0c;探讨其背后的技术实现和市场策略。需求分析&#xff1a;明确目标用户与市场…

分层-三层架构

文章目录介绍代码拆分Dao层server层controller层运行结果介绍 在我们进行程序设计以及程序开发时&#xff0c;尽可能让每一个接口、类、方法的职责更单一些&#xff08;单一职责原则&#xff09;。 单一职责原则&#xff1a;一个类或一个方法&#xff0c;就只做一件事情&#…

Vue2 VS Vue3

vue3 是的&#xff0c;Vue 3 确实取消了基于 JavaScript 原型的 Vue 和 VueComponent 构造函数&#xff08;即你提到的 vm 和 vc&#xff09;&#xff0c;取而代之的是一种完全不同的、基于普通对象和代理&#xff08;Proxy&#xff09;的实例管理方式。 这是一个颠覆性的改变…

Vue3入门到实战,最新版vue3+TypeScript前端开发教程,Vue3简介,笔记02

笔记02 一、Vue3简介 1.1、Vue3发布日期&#xff1a; 2020年9月18日 1.2、Vue3做了哪些升级&#xff1a; 1.2.1、性能的提升 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 打包大小减少41%初次渲染快55%更新渲染快133%内容减少54% 1.2.2、源码的优化…

.net core webapi/mvc阿里云服务器部署 - 错误解决

错误及解决方案缺少web.config配置HTTP 错误 500.19 - Internal Server Error检查 IIS 配置1. 确保 .NET Core Hosting Bundle 已安装2. 检查 应用程序池 配置3. 检查 IIS MIME 类型检查文件权限1. 确保 IIS 用户 有权限访问网站目录2. 检查 web.config 文件权限启用详细错误日…

多输入(input)多输出(output)验证

#作者&#xff1a;程宏斌 文章目录前言Flb 1.9.4 INCLUDE配置测试测试方案测试配置文件测试命令Flb 3.0.2 INCLUDE配置测试测试方案测试配置文件启动命令结论结论一&#xff1a;结论二&#xff1a;前言 需要设计并执行一组测试用例&#xff0c;这些测试用例将包括以子文件形式…

行业学习【电商】:垂直电商如何理解?以专业宠物平台为例

声明&#xff1a;以下部分内容含AI生成 “宠物等爱好者的专业平台”指的是垂直电商的一个具体例子。 “垂直电商” 就是指不卖所有东西&#xff0c;只深耕某一个特定领域&#xff08;即“垂直”领域&#xff09;的电商平台。 “宠物爱好者的专业平台”就是这样一个专门为养宠…

GPT(Generative Pre-trained Transformer)模型架构与损失函数介绍

目录 一、核心架构&#xff1a;Transformer Decoder 1. 核心组件&#xff1a;仅解码器&#xff08;Decoder-Only&#xff09;的堆叠 2. 输入表示&#xff1a;Token 位置 3. 输出 二、训练过程&#xff1a;两阶段范式 阶段一&#xff1a;预训练&#xff08;Pre-training&…

GitHub 热榜项目 - 日榜(2025-09-10)

GitHub 热榜项目 - 日榜(2025-09-10) 生成于&#xff1a;2025-09-10 统计摘要 共发现热门项目&#xff1a;15 个 榜单类型&#xff1a;日榜 本期热点趋势总结 本期GitHub热榜呈现三大技术热点&#xff1a;LLM智能体应用爆发&#xff08;如parlant、AutoAgent&#xff09;&a…

论文阅读:arxiv 2023 Large Language Models are Not Stable Recommender Systems

总目录 大模型相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 https://arxiv.org/pdf/2312.15746 速览 破解大语言模型在推荐系统中的不稳定性 该论文聚焦于大语言模型&#xff08;LLMs&#xff09;在推荐系统中的应用问题&#xff0c;指出…

Linux的使用——FinalShell下载使用及连接云服务器的教程

一、注册免费阿里云服务器 1. 进入阿里云服务器官网 阿里云-计算&#xff0c;为了无法计算的价值https://www.aliyun.com/?spm5176.ecscore_server.console-base_top-nav.dlogo.39144df5uvPLOm 2. 点击免费试用 这里我已经试用过了&#xff0c;大家选择合适的云服务器点击立…

如何清理 Docker 占用的巨大磁盘空间

我相信很多人在使用 Docker 一段时间后&#xff0c;都会遇到一个常见问题&#xff1a;磁盘空间被迅速吃光&#xff0c;尤其是在进行频繁的镜像构建、测试和运行容器时。以我自己为例&#xff0c;在 Ubuntu 24.04设备上&#xff0c;docker system df -v 一看&#xff0c;Docker …

【CMake】缓存变量

目录 一. 缓存变量 二.创建缓存变量 2.1.使用set()来创建缓存变量 2.2.使用FORCE参数来覆盖缓存变量 2.2.1.示例1——不带force的set是不能覆盖已经存在的缓存变量的 2.2.2.示例2——带force的set才能覆盖已经存在的缓存变量 2.2.3.对比示例 2.3.命令行 -D 创建/覆盖缓…

vue2使用若依框架动态新增tab页并存储之前的tab页的操作

1. 应用场景&#xff1a;点击历史记录&#xff0c;要比较两个tab页的内容时&#xff0c;需要做到切换tab页来回看左右对数据对比。2.开发难点若依项目正常是把路由配置到菜单管理里&#xff0c;都是设定好的。不过它也给我们写好了动态新增tab页的方&#xff0c;需要我们自己来…

论文阅读-SelectiveStereo

文章目录1 概述2 模块2.1 SelectiveIGEV和IGEV的差异2.2 上下文空间注意力2.2.1 通道注意力2.2.2 空间注意力2.3 SRU3 效果参考资料1 概述 本文主要结合代码对Selective的创新点进行针对性讲解&#xff0c;相关的背景知识可以参考我写的另两篇文章论文阅读-RaftStereo和论文阅…