当前位置: 首页 > news >正文

LinuxOS阻塞队列模型(单生产者单消费者)

前言

作者数据结构还剩下AVL树,红黑树等结构未复习,准备同时推进操作系统和网络的代码

先从阻塞队列入手,这是一个很经典的生产与消费模型

1. 阻塞队列的数据量上限

const static int gcap = 10; ... private: std::queue<T> _q; int _cap;

阻塞队列类中会有一个成员变量_cap,代表阻塞队列最多数据的上限,决定了生产者最多能往阻塞队列中生产多少数据

2. 条件变量在阻塞队列中的作用

pthread_cond_t _producer_cond; pthread_cond_t _consumer_cond;

条件变量可以同步生产和消费者,比如队列为空时,消费者进入等待状态,然后生产者生产以后唤醒消费者,让消费者能够及时处理数据,这就是同步

3. 记录等待中的生产者或者消费者的数量

int _pwait_num; int _cwait_num;

比如先分析_pwait_num的作用,只有当有等待的生产者时(_pwait_num > 0),消费者才会进行唤醒生产者

若没有等待的生产者(_pwait_num == 0),消费者则不会执行pthread_cond_single代码,节省了计算资源

4. queue的push是拷贝,所以Equeue的参数可以有const T&

void Equeue(const T &in) { pthread_mutex_lock(&_mutex); ... _q.push(in);

_q.push(in)只是把in的值赋值给push函数里面的形式参数,但是不修改in变量的值,所以Equeue的传参可以用const T &in,这样既能传入左值,又能传入右值

5. 单生产者和单消费者,既有竞争关系,又有同步关系

->竞争关系

生产者和消费者会竞争同一把锁

->同步关系

生产者进入等待后,消费者消费完,会唤醒生产者,让生产者继续生产,即同步

6. 阻塞队列的出队对应着消费,入队对应着生产

出队就是把队列里面的数据拿出去给消费者,即消费者消费掉

入队就是把数据放入队列,即生产者进行生产

阻塞队列相当于一个缓冲区

7. static_cast是一个类型转换运算符

static_cast会把()里面的值,安全地强转为<>里面的类型

8. pthread_cond_wait会归还锁,被唤醒后重新申请锁,继续从wait处运行

while (isFull()) { _pwait_num++; pthread_cond_wait(&_producer_cond, &_mutex); _pwait_num--; }

比如入队(生产)逻辑,在阻塞队列满的时候,生产者进入条件变量等待,除了传递第一个条件变量参数,还要传递一把锁

这把锁就是这个线程在等待之前要释放的锁

线程被唤醒后会重新竞争申请锁,然后继续从pthread_cond_wait处运行

9. pthread_create中的线程传递用了取地址,而pthread_join中没有取地址,只是传拷贝

pthread_t p, c; pthread_create(&p, nullptr, Produce, bq); pthread_create(&c, nullptr, Consume, bq); pthread_join(p, nullptr); pthread_join(c, nullptr);

测试代码中,因为pthread_create中是要给p赋值的,所以要把p的地址传进去

而pthread_join是用来回收线程的,而不再改变p的值,所以直接传形参即可

10. 生产者因为队列为满进入的等待需要用while循环,消费者因为队列为空进入的等待也需要用while循环

while (isFull()) { _pwait_num++; pthread_cond_wait(&_producer_cond, &_mutex); _pwait_num--; }

这是在多生产者、多消费者下会出现的问题,比如有多个生产者被唤醒,然后它们就要竞争同一把锁,而被唤醒的生产者只有一个能竞争到锁。其余的就要继续进入一个while循环,去等待

总体实现

#pragma once #include <pthread.h> #include <queue> namespace BlockQueueModule { const static int gcap = 10; template <class T> class BlockQueue { private: bool isFull() { return _q.size() == _cap; } bool isEmpty() { return _q.empty(); } public: BlockQueue(int cap = gcap) :_cap(cap) ,_pwait_num(0) ,_cwait_num(0) { pthread_mutex_init(&_mutex, nullptr); pthread_cond_init(&_producer_cond, nullptr); pthread_cond_init(&_consumer_cond, nullptr); } void Equeue(const T &in) { pthread_mutex_lock(&_mutex); while (isFull()) { _pwait_num++; pthread_cond_wait(&_producer_cond, &_mutex); _pwait_num--; } _q.push(in); if (_cwait_num) { pthread_cond_signal(&_consumer_cond); } pthread_mutex_unlock(&_mutex); } void Pop(T *out) { pthread_mutex_lock(&_mutex); while (isEmpty()) { _cwait_num++; pthread_cond_wait(&_consumer_cond, &_mutex); _cwait_num--; } *out = _q.front(); _q.pop(); if (_pwait_num) { pthread_cond_signal(&_producer_cond); } pthread_mutex_unlock(&_mutex); } ~BlockQueue() { pthread_mutex_destroy(&_mutex); pthread_cond_destroy(&_producer_cond); pthread_cond_destroy(&_consumer_cond); } private: std::queue<T> _q; int _cap; pthread_mutex_t _mutex; pthread_cond_t _producer_cond; pthread_cond_t _consumer_cond; int _pwait_num; int _cwait_num; }; }

测试代码

#include "BlockQueue.hpp" #include <unistd.h> #include <stdio.h> using namespace BlockQueueModule; void* Produce(void* args) { BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args); int data = 0; while (true) { sleep(2); bq->Equeue(data); printf("生产者生产数据: %d\n", data); data++; } } void* Consume(void* args) { BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args); while (true) { int data; bq->Pop(&data); printf("消费者消费数据: %d\n", data); } } int main() { BlockQueue<int> *bq = new BlockQueue<int>(); pthread_t p, c; pthread_create(&p, nullptr, Produce, bq); pthread_create(&c, nullptr, Consume, bq); pthread_join(p, nullptr); pthread_join(c, nullptr); delete bq; return 0; }

测试结果

./main
生产者生产数据: 0
消费者消费数据: 0
生产者生产数据: 1
消费者消费数据: 1
生产者生产数据: 2
消费者消费数据: 2
生产者生产数据: 3
消费者消费数据: 3
生产者生产数据: 4
消费者消费数据: 4
生产者生产数据: 5
消费者消费数据: 5
^C

http://www.jsqmd.com/news/809321/

相关文章:

  • Axure RP中文界面解决方案:告别英文障碍,5分钟实现高效设计体验
  • 从‘Temporary failure resolving’到流畅pip install:一次搞定Ubuntu系统级网络配置
  • 【ChatGPT YouTube内容规划终极避坑指南】:避开平台限流红线、规避AI检测、锁定搜索热词的6维校验模型
  • Photoshop图层批量导出终极指南:3倍速免费工具让设计工作更高效
  • 饥荒联机版MOD-杀生丸:从妖力核心到神装共鸣的深度玩法解析
  • 企业AI成本为什么总是失控?Token计量与费用归因体系设计
  • Unity实战:用RenderTexture和LineRenderer做个刮刮乐小游戏(附完整项目源码)
  • CS Demo Manager:终极免费CS比赛回放分析与战术提升完全指南
  • STM32 PID温控:如何用80元开发板实现±0.5°C的精准温度控制
  • SFI立昌ESD/TVS二三极原厂原装一级代理分销经销
  • MediaSession与MediaController
  • 终极免费图片去重神器:3步快速释放存储空间的完整解决方案
  • CodeGraph:构建代码知识图谱,实现AI编程助手从搜索到推理的范式升级
  • Node.js后端接入Claude的5大避坑清单(2024最新OpenRouter/Vercel AI SDK适配实录)
  • 冷热量计十大品牌推荐,看这一篇就够了 - 仪表人叶工
  • 【30岁还能学网工吗?10年高级网络工程师分享】
  • 59-260512 AI 科技日报(Gemini 视频模型曝光、DeepSeek V4 限时免费、OpenAI 布局企业部署)
  • 手把手教你用百度地图API在EduCoder上绘制共享单车轨迹(附完整代码)
  • 5分钟快速上手:Windows平台最高效的Android应用安装器终极指南
  • 斐讯N1盒子Armbian系统调优:从U盘启动到EMMC固化的全流程精解
  • DVWA靶场实战:手把手教你解决allow_url_include报错(PHPStudy/XAMPP通用)
  • 3步轻松破解Cursor AI助手限制:免费使用Pro功能的终极解决方案
  • 观澜墅二手房价格走势观察:供需关系与价值评估 - 品牌2026
  • 使用pip安装youget并配置Taotoken大模型API进行视频分析
  • NotebookLM如何重构你的NLP工作流,72小时实现从零标注到可部署模型闭环
  • java对象不被GC回收的情况
  • 升级JDK8 spring5的项目至 boot4+jdk26过程记录(一)
  • 别再让 AI 单兵作战了:Claude Code + Codex CLI 组成“AI 开发小队“
  • 2026年树脂混凝土泵站生产厂家权威推荐榜单:树脂混凝土一体化泵站/树脂混凝土沉井泵站/树脂混凝土预制泵站源头厂家精选 - 泵站报价15613348888
  • APK Installer:Windows上安装Android应用的终极指南,简单三步轻松搞定