Linux OS:基于阻塞队列的生产者消费者模型

news2024/9/21 5:32:04

Linux OS:基于阻塞队列的生产者消费者模型

  • 前言
  • 一、阻塞队列的大致框架
  • 二、生产者向阻塞队列中生产数据
  • 三、消费者获取阻塞队列中数据
  • 四、总体生产和消费思路及测试代码
    • 4.1 单生产单消费
    • 4.2 多生产多消费
  • 五、所以代码

前言

 阻塞队列是一种常用于实现生产者消费者模型的数据结构。和普通队列不同的时,队列存在上限,当队列未满时,往队列中插入数据的操作会被阻塞;当队列为空时,从队列中获取数据的操作也会被阻塞!

  • 在Linux OS:线程封装 | RAII封装锁 | 随机数运算任务封装中,对锁进行了封装,后续加锁解锁操作都基于此!

一、阻塞队列的大致框架

 对于阻塞队列BlockQueue我们首先需要一个普通队列作为生产消费场所!阻塞队列存在上限,我们也需要指明。

 消费者和生产者之间都需要向队列进行操作(插入和获取数据),即访问临界区资源。生产者和消费者并发访问临界资源时会导致多执行流数据不一致问题。所以我们需要对队列进行加锁!

 对于生产者来说,空间就是资源;对于消费者来说,队列中的数据才是资源。在阻塞队列中,只要队列没有到达上限,生产者就可以一直生产;只要队列中有数据,消费者就可以一直消费数据!但存在队列未满,为空情况。此时生产者和消费者会疯狂进行加锁和解锁,访问无意义的临界资源!所以为了防止这种情况出现,我们在生产者和消费者中添加条件变量!当队列为空时,消费者阻塞等待;当队列未满时,生产者阻塞等待!并且生产者生产数据后,直接通知消费者进行消费;消费者消费完后,立即通知生产者生产。

 然后我们需要在构造函数中对锁和条件变量进行初始化,析构时进行回收!具体如下:

const int defaultSize = 5; // 阻塞队列默认大小

template <class T>
class BlockQueue
{
public:
    BlockQueue(int cap = defaultSize)
        : _capacity(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
    }
    
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_p_cond);
        pthread_cond_destroy(&_c_cond);
    }

private:
    std::queue<T> _q;
    int _capacity; // 阻塞队列上限大小
    pthread_mutex_t _mutex;
    pthread_cond_t _p_cond; // 生产者使用
    pthread_cond_t _c_cond; // 消费者使用
};

二、生产者向阻塞队列中生产数据

 生产者向临界区(队列资源)中插入数据时,先申请锁,然后判断队列中是否未满。如果队列未满,此时生产者在条件变量出进行等待,一旦消费者消费数据发送信号后就会被唤醒,继续向后执行!当生产者插入完数据后,通知消费者消费,即唤醒消费者的条件变量!最后就是是否锁资源了!

 bool Full()
 {
     return _q.size() == _capacity;
 }

 void Push(T &in)
 {
     {
         LockGuard lock(&_mutex);
         // pthread_mutex_lock(&_mutex);
         while (Full())
         {
             pthread_cond_wait(&_p_cond, &_mutex);
         }
         _q.push(in);
         pthread_cond_signal(&_c_cond); // 通知消费者消费
         // pthread_mutex_unlock(&_mutex);
     }
 }

三、消费者获取阻塞队列中数据

 消费者行为和生产者类似:申请锁,判断队列中是否存在数据(不存在,消费者在条件变量处阻塞等待),获取队列数据,唤醒生产者条件变量、释放锁!

bool Empty()
{
    return _q.size() == 0;
}

void Pop(T *out)
{
    {
        LockGuard lock(&_mutex);
        // pthread_mutex_lock(&_mutex);
        while (Empty())
        {
            pthread_cond_wait(&_c_cond, &_mutex);
        }
        *out = _q.front();
        _q.pop();
        pthread_cond_signal(&_p_cond); // 通知生成者生产
        // pthread_mutex_unlock(&_mutex);
    }
}

四、总体生产和消费思路及测试代码

4.1 单生产单消费

 在阻塞队列BlockQueue中我们封装了向队列中插入数据和获取数据接口,并在文章开始封装了一个随机数运算任务封装类。

 下面我们在主线程中分布创建一个生产者和消费者线程,并通过参数方式让两个线程看到同一份公共资源 —— 阻塞队列。然后生产者每隔一秒生产一个任务放到阻塞队列中(两个随机数加随机运算符构成的任务);消费者直接获取阻塞队列中的数据,并进行处理!

【测试代码】:

const std::string opers = "+-*[/{%]0";

void *Productor(void *args)
{
    BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
    while (true)
    {
        sleep(1);
        int data_x = rand() % 10;
        usleep(1000);
        int data_y = rand() % 10;
        char op = opers[rand() % opers.size()];
        Task t(data_x, data_y, op); // 向阻塞队列中生产数据
        std::cout << "Productor push task: " << t.PrintTask() << std::endl;
        bq->Push(t);
    }
}

void *Consumer(void *args)
{
    BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
    while (true)
    {
        Task t;
        bq->Pop(&t); // 获取数据
        t(); // 对数据进行处理
        std::cout << "Consumer do task done: " << t.PrintResult() << std::endl;
    }
}

int main()
{
    srand(time(nullptr) ^ pthread_self() ^ getpid());
    pthread_t p, c;
    BlockQueue<Task> *bq = new BlockQueue<Task>(); // 阻塞队列

    pthread_create(&p, nullptr, Productor, (void *)bq); // 创建生产者线程
    pthread_create(&c, nullptr, Consumer, (void *)bq);  // 创建消费者线程

    pthread_join(p, nullptr);
    pthread_join(c, nullptr);
    return 0;
}

【运行结果】:请添加图片描述

4.2 多生产多消费

单生产单消费多生产多消费一样,不同之处在于我们需要通过数组将生产者和消费者管理起来罢了!
【伪代码】:

int main()
{
    srand(time(nullptr) ^ pthread_self() ^ getpid());
    pthread_t p[3], c[2]; // 管理多生产者和多消费者
    BlockQueue<Task> *bq = new BlockQueue<Task>(); // 阻塞队列

    pthread_create(&p[0], nullptr, Productor, (void *)bq); // 创建生产者线程
    pthread_create(&p[1], nullptr, Productor, (void *)bq); // 创建生产者线程
    pthread_create(&p[2], nullptr, Productor, (void *)bq); // 创建生产者线程
    pthread_create(&c[0], nullptr, Consumer, (void *)bq);  // 创建消费者线程
    pthread_create(&c[1], nullptr, Consumer, (void *)bq);  // 创建消费者线程

    pthread_join(p[0], nullptr);
    pthread_join(p[1], nullptr);
    pthread_join(p[2], nullptr);
    pthread_join(c[0], nullptr);
    pthread_join(c[1], nullptr);

    return 0;
}

五、所以代码

gitee: Linux OS:基于阻塞队列的生产者消费者模型

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

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

相关文章

大模型微调技术PEFT

1. 横向对比 总体概览&#xff1a; 方法Transformer 中如何应用特点Prompt Tuning在输入到Transformer的input文本中添加Prompthard 模式prompt CO-STARP-tuning在输入到Transformer的embedding 层添加Promptsoft 模式 promptAdapter Tuning在 Transformer 中间层插入 Adapt…

MATLAB代码|蚁群算法|计算二元函数最大值

总述 蚁群方法求解二元函数的最大值。蚂蚁群先随机分布在定义域内&#xff0c;如下&#xff1a; 计算结束后&#xff0c;得到如下的分布&#xff0c;再计算分布均值&#xff0c;得到此时的自变量取值&#xff0c;因为是二元函数&#xff0c;所以有两个自变量&#xff0c;带入…

Qt 实战(9)窗体 | 9.1、QWidget

文章目录 一、QWidget1、定义与概念2、继承体系3、常用特性3.1、事件处理3.2、布局管理3.3、子控件与父子关系3.4、右键菜单 4、注意事项5、总结 前言&#xff1a; 在Qt这一强大的跨平台C图形用户界面应用程序开发框架中&#xff0c;QWidget扮演着至关重要的角色。作为所有用户…

【日记】为啥家族原发性高血压的人还喜欢喝酒啊……(442 字)

正文 今天跟人吵了一下午架&#xff0c;因为有一张报表换了新表&#xff0c;所有人都不知道怎么报。上级行一个想法&#xff0c;我一个想法。吵完都发现对方说得有道理&#xff0c;于是决定明天问省分行。难绷。草台班子。 鱼儿说他最近喜欢上了喝酒。我们劝他的同时&#xff0…

太阳光度计CE-318数据处理

太阳光度计CE-318数据处理 备注&#xff1a;处理公式 在我国近海&#xff0c;α的值在0到3之间&#xff0c;所以他们相对误差最大不超过25%&#xff0c;而通过查阅相关资料&#xff0c;北京地区α的值可以近似的取1.665。 大气是不断运动的&#xff0c;气溶胶在短时间内也可…

【网络安全】https协议的加密方案避免中间人攻击(MITM攻击)导致的数据泄露风险

目录 引言 概念准备 中间人 加密 数据摘要 && 数据指纹 数字签名 密钥加密 中间人攻击 CA证书 https加密的解决方案 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 引言 http在应用层协议中是明文传输协议&#xff0c;它是通信双方传输数据时的一种约定。【…

超声波便携式气象站:精准监测,随身携带

在追求高效与便捷的今天&#xff0c;超声波便携式气象站以其独特魅力脱颖而出&#xff0c;成为众多行业及户外爱好者的理想选择。超声波便携式气象站采用多采集装置一体式设计&#xff0c;不仅实现了体积的小型化与重量的轻量化&#xff0c;更便于用户随身携带&#xff0c;随时…

期权杠杆与期货杠杆的区别是什么?

期权与股指期货在杠杆性上展现出截然不同的特性&#xff0c;这些特性对投资者的策略选择具有深远影响。首先&#xff0c;股指期货采用保证金制度&#xff0c;其杠杆比例是恒定的&#xff0c;无论市场如何波动&#xff0c;投资者在月初设定的十倍杠杆到月尾仍保持不变。相比之下…

STM32常见的下载方式有三种

经过对比&#xff0c;推荐使用 SWD下载&#xff0c;只需要一个仿真器&#xff08;如jLINK、ST LINK、 CMSIS DAP 等&#xff09;&#xff0c;比较方便。 不推荐使用串口下载&#xff08;速度慢、无法仿真和调试&#xff09;和 JTAG 下载&#xff08;占用 IO 多&#xff09;。

Java语言程序设计基础篇_编程练习题*16.7 (设置时钟的时间)

*16.7 (设置时钟的时间) 编写一个程序&#xff0c;显示一个时钟&#xff0c;并通过在三个文本域中输入小时、分钟和秒 钟来设置时钟的时间&#xff0c;如图16-38b 所示。使用程序清单14-21的ClockPane改变时钟大小使其居于面板中央 习题思路 实例化一个ClockPane(在程序清单1…

【网络安全渗透测试零基础入门必知必会】之什么是文件包含漏洞分类(非常详细)零基础入门到精通,收藏这一篇就够了

一、前言 这是大白给粉丝盆友们整理的网络安全渗透测试入门阶段文件包含渗透与防御第1篇。 本文主要讲解什么是文件包含漏洞、本地文件包含漏洞 喜欢的朋友们&#xff0c;记得给大白点赞支持和收藏一下&#xff0c;关注我&#xff0c;学习黑客技术。 一、什么是文件包含漏洞…

the request was rejected because no multipart boundary was found

文章目录 1. 需求描述2. 报错信息3. 探索过程1. 使用postman 排除后端错误2. 搜索网上的解决方法3. 解决方法 1. 需求描述 想要在前端上传一个PDF 发票&#xff0c;经过后端解析PDF之后&#xff0c;将想要的值自动回填到对应的输入框中 2. 报错信息 org.apache.tomcat.util.…

VSCode远程调试Linux程序

VS 安装CodeRunner插件 菜单→添加配置→lunch.json中设置如下&#xff1a; program填入要调试的文件 {"version": "0.2.0","configurations": [{"name": "gdb renderPng","type": "cppdbg","re…

为什么某央企可以抓到红队攻击,而你不行?

国家HVV行动从2016年到2024年已经是第9年了&#xff0c;HVV行动目的就是保卫国家关键基础设施的网络安全行动&#xff0c;更是一场实战化的网络攻击与防御实战&#xff0c;这些年来红队攻击手段层出不穷&#xff0c;最为典型的就是 0/N Day、弱口令、社工钓鱼等&#xff0c;也极…

北京青蓝智慧科技ITSS服务经理:长安链ChainBridge“链桥”问世 加速国家级区块链网络互联互通

8月5日&#xff0c;据国家区块链技术创新中心消息&#xff0c;我国首个完全自主控制的区块链软硬件技术系统——长安链&#xff0c;正式推出了全场景技术平台ChainBridge“链桥”。 此平台能够支持所有异构和同构的区块链进行协作&#xff0c;满足跨领域、跨地域、跨行业及跨层…

用Java手写jvm之模拟方法调用指令invokexxx和方法返回指令xreturn

写在前面 源码 。 本文一起看下方法调用相关的指令invokexxx以及方法返回&#xff08;栈帧弹出线程栈&#xff09;相关的指令xReturn 。 1&#xff1a;正文 因为invokexxx指令和普通的指令不同&#xff0c;会创建一个新的栈帧&#xff0c;并压倒操作数栈中&#xff0c;所以我…

《黑神话:悟空》在PS5上优化得不错 能达到2K/60帧

《黑神话&#xff1a;悟空》是今年最受玩家期待的游戏之一&#xff0c;但许多粉丝担心该作优化不佳&#xff0c;因为其使用的是虚幻5引擎。虚幻5引擎会导致性能问题出现&#xff0c;游戏 科学的新作也将面临同样问题。但有新报告称&#xff0c;《黑神话》PS5版优化得相当不错&a…

UE 后期处理

UE4后期处理材质的一些应用&#xff08;上&#xff09; - 哔哩哔哩 (bilibili.com) UE4后期处理材质的一些应用&#xff08;下&#xff09; - 哔哩哔哩 (bilibili.com) 后期处理材质的作用 后期处理材质使您能够设置与后期处理一起使用的材质&#xff0c;以创建破坏的视觉屏幕…

免费【2024】springboot 二手图书交易系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

PyCharm找不到Python了咋办

Python发生了重装的&#xff0c;且新的路径和原有路径不同&#xff0c;就会出现如下的错误&#xff1a; 解决办法&#xff1a; 点开PyCharm菜单的File/Setting 然后&#xff1a; 有上图的提示&#xff0c;说明需要将原来的venv进行清空。 如此操作之后&#xff0c;原来的红色…