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

c++生产者消费者者模式笔记-1阻塞问题

生产者消费者模式是并发编程的核心模式之一核心是想要提高程序的运行效率。这里记录一下自己的思考使用通俗易懂的语言和以日志记录为例解读生产者消费者模式并实现生产者消费者模式。将生产者消费者模式的核心内容划分为三个问题阻塞问题、内存积压问题、cpu空转问题。这里是第一章阻塞问题。阻塞问题生产者消费者模式首先就是要解决相互阻塞的问题。在编程过程中循环是普遍存在的循环内部普遍来说是顺序执行顺序结构可以分为上下两个部分。程序上方的输出数据往往是程序下方的输入从数据的角度看程序上方就是生产过程称为生产者程序下方就是消费过程称为消费者。这种结构下生产者和消费者之间必然相互等待如果两个两者耗时较长那么就会导致程序运行效率降低这就称之为程序之间相互阻塞。实际场景日志记录系统就是一个典型的生产者消费者模式。项目运行时需要循环记录日志在循环内部程序上方是生成日志程序下方是写入日志文件。所以生产者就是生成字符串消费者是写入日志文件。代码实现程序在同一个线程中循环执行循环体中使用顺序结构生产一个字符串紧接着在磁盘中写入一个字符串。这就是同步耦合实现这样会导致生产者生产和消费者相互等待即相互阻塞。void sync_log() { std::cout同步耦合日志系统std::endl; std::ofstream log_file; std::string log_pathlog1.txt; std::chrono::high_resolution_clock::time_point start std::chrono::high_resolution_clock::now(); for(int i0;i10;i) { log_file.open(log_path,std::ios::app); //cpu内存处理数据 std::chrono::high_resolution_clock::time_point t1 std::chrono::high_resolution_clock::now(); //大量数据 std::string large_data(2048, x); // 2KB 数据 //时间记录 auto time std::chrono::system_clock::now(); auto time_t std::chrono::system_clock::to_time_t(time); std::string content_timestd::ctime(time_t); std::cout日志记录时刻content_time; std::string content[content_time] large_data; std::chrono::high_resolution_clock::time_point t2 std::chrono::high_resolution_clock::now(); //用户缓存区写日志 log_filecontentstd::endl; //写磁盘 log_file.close(); std::chrono::high_resolution_clock::time_point t3 std::chrono::high_resolution_clock::now(); std::cout处理业务耗时std::chrono::duration_caststd::chrono::microseconds(t2-t1).count()微秒std::endl; std::cout写日志耗时std::chrono::duration_caststd::chrono::microseconds(t3-t2).count()微秒std::endlstd::endl; } // 写入磁盘 std::chrono::high_resolution_clock::time_point end std::chrono::high_resolution_clock::now(); std::cout生成日志和io相互阻塞std::endl; std::cout耗时std::chrono::duration_caststd::chrono::microseconds(end-start).count()msstd::endl; } int main() { sync_log(); return 0; }运行结果同步耦合日志系统 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时2101微秒 写日志耗时141微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时68微秒 写日志耗时112微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时155微秒 写日志耗时87微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时86微秒 写日志耗时111微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时78微秒 写日志耗时100微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时56微秒 写日志耗时95微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时59微秒 写日志耗时68微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时45微秒 写日志耗时85微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时52微秒 写日志耗时88微秒 日志记录时刻Fri May 15 10:20:42 2026 处理业务耗时66微秒 写日志耗时107微秒 生成日志和io相互阻塞 耗时8398ms结果分析每一次循环都需要生成一个字符串除了在第一次循环其他每一次都需要等待写入日志完成后才能生成字符串同理写入磁盘也类似包括第一次循环每一次都需要等待生成字符串完成后才能写入磁盘。这就是生产者和消费者速度相互阻塞生成字符串是生产者写入磁盘是消费者两者相互等待导致程序整体效率低。解决阻塞生产者和消费者相互阻塞如何解决同步耦合深入分析阻塞问题的根本原因在于同步耦合即调用方必须等待被调用方完成才能继续执行的模式。在生产者消费者视角下看同步耦合来自于生产者和消费者在同一个线程、处于同一个顺序结构中上下游的位置当循环时前后者就会相互制约。异步解耦合既然阻塞时同步耦合导致的那么解决就需要异步解耦合。即调用方不需要等待被调用方完成就可以继续执行的模式。在生产者消费者视角下看就需要将生产者和消费者放在两个独立的线程中运行这样两者不相互依赖同时运行。代码实现分离生产者和消费者线程生产者只需要生成日志字符串不需要关心写入日志文件消费者只需要写入日志文件不需要关心生成日志字符串但是这样又会产生新的问题生产者生产数据消费者消费数据两者如何关联起来呢为了解决这些问题我们可以创建共享变量作为通信方式生产者和消费者能同时访问到这个变量生产者生产数据后将数据放入共享变量消费者消费数据时从共享变量中取出数据进行消费由于是多线程访问共享变量所以需要引入互斥锁当生产者生产数据时消费者无法访问共享变量当消费者消费数据时生产者无法访问共享变量避免了数据竞争保证了数据安全这样生产者和消费者就互不依赖同时运行解决了阻塞问题这样就是异步解耦合程序完成相同的功能但是效率更高。void sync_log() { std::cout同步耦合日志系统std::endl; std::ofstream log_file; std::string log_pathlog1.txt; std::chrono::high_resolution_clock::time_point start std::chrono::high_resolution_clock::now(); for(int i0;i10;i) { log_file.open(log_path,std::ios::app); //cpu内存处理数据 // std::chrono::high_resolution_clock::time_point t1 std::chrono::high_resolution_clock::now(); //大量数据 std::string large_data(2048, x); // 2KB 数据 //时间记录 auto time std::chrono::system_clock::now(); auto time_t std::chrono::system_clock::to_time_t(time); std::string content_timestd::ctime(time_t); // std::cout日志记录时刻content_time; std::string content[content_time] large_data; std::chrono::high_resolution_clock::time_point t2 std::chrono::high_resolution_clock::now(); //用户缓存区写日志 log_filecontentstd::endl; //写磁盘 log_file.close(); // std::chrono::high_resolution_clock::time_point t3 std::chrono::high_resolution_clock::now(); // std::cout处理业务耗时std::chrono::duration_caststd::chrono::microseconds(t2-t1).count()微秒std::endl; // std::cout写日志耗时std::chrono::duration_caststd::chrono::microseconds(t3-t2).count()微秒std::endlstd::endl; } // 写入磁盘 std::chrono::high_resolution_clock::time_point end std::chrono::high_resolution_clock::now(); std::cout生成日志和io相互阻塞std::endl; std::cout耗时std::chrono::duration_caststd::chrono::microseconds(end-start).count()msstd::endl; } void async_log() { std::cout异步解耦日志系统,不再相互阻塞std::endl; std::ofstream log_file; std::string log_pathlog2.txt; // 缓存 std::dequestd::string log_buffer; // 停止标志 std::atomicbool stop_flag(false); // 加锁避免数据竞争 std::mutex log_mutex; // cpu内存处理数据 auto log_data_func[log_buffer,stop_flag,log_mutex](){ int count1; std::chrono::high_resolution_clock::time_point start std::chrono::high_resolution_clock::now(); while(true) { // std::chrono::high_resolution_clock::time_point t1 std::chrono::high_resolution_clock::now(); //创建数据 std::string large_data(2048, x); // 4KB 数据 // std::this_thread::sleep_for(std::chrono::milliseconds(100)); //时间记录 auto time std::chrono::system_clock::now(); auto time_t std::chrono::system_clock::to_time_t(time); std::string content_timestd::ctime(time_t); // std::cout生成日志content_timestd::endl; std::string content[content_time] large_data; //写入缓存 std::unique_lockstd::mutex lock(log_mutex); log_buffer.push_back(content); lock.unlock(); // std::chrono::high_resolution_clock::time_point t2 std::chrono::high_resolution_clock::now(); // std::cout处理业务耗时std::chrono::duration_caststd::chrono::microseconds(t2-t1).count()微秒std::endlstd::endl; count; if(count10) { std::chrono::high_resolution_clock::time_point t_10 std::chrono::high_resolution_clock::now(); std::cout生成10条日志耗时std::chrono::duration_caststd::chrono::microseconds(t_10-start).count()msstd::endl; } if (stop_flag) { break; } }; std::cout生成日志结束std::endl; std::cout总共生成数据量count条std::endl; }; std::thread thread_log_data(log_data_func); // 日志写入磁盘 auto log_disk_func[log_buffer,log_file,log_path,stop_flag,log_mutex](){ //计时开始 std::chrono::high_resolution_clock::time_point start std::chrono::high_resolution_clock::now(); int count1; while(true) { //判断退出循环 if (count10) { break; } if(!log_buffer.empty()) { std::chrono::high_resolution_clock::time_point t1 std::chrono::high_resolution_clock::now(); //打开文件 log_file.open(log_path,std::ios::app); //写日志 std::unique_lockstd::mutex lock(log_mutex); auto datalog_buffer.front(); log_buffer.pop_front(); lock.unlock(); log_filedatastd::endl; //关闭文件 log_file.close(); std::chrono::high_resolution_clock::time_point t2 std::chrono::high_resolution_clock::now(); // std::cout写日志耗时std::chrono::duration_caststd::chrono::microseconds(t2-t1).count()微秒std::endl; count; } } //通知生产进程结束 stop_flag true; //计时结束 std::chrono::high_resolution_clock::time_point end std::chrono::high_resolution_clock::now(); std::coutio耗时std::chrono::duration_caststd::chrono::microseconds(end-start).count()msstd::endl; }; std::thread thread_log_disk(log_disk_func); thread_log_data.join(); thread_log_disk.join(); } int main() { // 同步阻塞问题 sync_log(); std::cout----------------std::endl; async_log(); return 0; }运行结果同步耦合日志系统 生成日志和io相互阻塞 耗时4978ms ---------------- 异步解耦日志系统,不再相互阻塞 生成10条日志耗时46ms io耗时3864ms 生成日志结束 总共生成数据量600条结果分析同步耦合日志系统生成日志和io相互阻塞记录10条日志到文件共耗时4978ms异步解耦日志系统生成日志和io不再相互阻塞记录10条日志到文件共耗时3864ms异步解耦合日志系统当10条日志记录结束时总共生成数据量都已经生成600条日志了同样是记录10条日志相互阻塞的方式耗时4978ms非阻塞的方式耗时3864ms非阻塞的方式耗时明显更短同样是记录10条日志相互阻塞的方式只生成了10条日志而非阻塞的方式生成了600条非阻塞的方式生成了更多的数据结论异步解耦合的方式以更短的时间生成了达到同样的记录效果并且生成了更多的数据说明异步解耦合的方式更高效

相关文章:

c++生产者消费者者模式笔记-1阻塞问题

生产者消费者模式是并发编程的核心模式之一,核心是想要提高程序的运行效率。 这里记录一下自己的思考,使用通俗易懂的语言,和以日志记录为例,解读生产者消费者模式,并实现生产者消费者模式。 将生产者消费者模式的核心…...

智能视频转PPT:3分钟实现视频内容自动提取的完整方案

智能视频转PPT:3分钟实现视频内容自动提取的完整方案 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 你是否曾为整理会议录像中的PPT内容而烦恼?手动暂停、截…...

【Perplexity健身计划搜索实战指南】:20年AI搜索专家亲授3大精准检索心法,错过再等一年

更多请点击: https://codechina.net 第一章:Perplexity健身计划搜索实战指南导论 Perplexity 是一款以推理深度和引用可追溯性见长的 AI 搜索工具,特别适合需要结构化、证据支撑型信息检索的场景。在健身领域,用户常面临计划泛滥…...

MC/DC覆盖率:从原理到实战,破解100%覆盖率的迷思与挑战

1. 项目概述:当“完美”成为负担在软件测试领域,尤其是对安全关键系统(比如航空航天、汽车电子、医疗设备)的验证,我们常常听到一个词:100%覆盖率。这听起来像是一个终极目标,一个完美的终点。但…...

MATLAB数据处理小技巧:用reshape函数把一维数组变成你想要的任意形状(附图像处理实例)

MATLAB数据处理实战:reshape函数的高效应用与图像处理案例 当你面对一堆杂乱无章的一维数据时,是否曾为如何将其整理成适合分析的格式而头疼?在MATLAB中,reshape函数就像一位魔术师,能够在不改变数据本质的情况下&…...

3分钟掌握京东自动抢购神器:告别“手慢无“的终极指南

3分钟掌握京东自动抢购神器:告别"手慢无"的终极指南 【免费下载链接】autobuy-jd 使用python语言的京东平台抢购脚本 项目地址: https://gitcode.com/gh_mirrors/au/autobuy-jd 还在为京东限时秒杀商品总是抢不到而烦恼吗?面对心仪的热…...

Keil C编译器字符串常量合并机制与内存优化

1. Keil C编译器中的字符串常量合并机制解析在嵌入式开发中,内存优化是一个永恒的话题。Keil C编译器(包括C51、C166和C251版本)提供了一项智能特性——自动合并重复的字符串常量。这个功能看似简单,但对资源受限的嵌入式系统而言…...

百考通AI智能聚类文献,告别碎片化罗列

撰写文献综述,是学术写作中承上启下的关键一步。它不仅要展示你对研究领域的了解程度,更要体现你的归纳能力、批判思维和问题意识。然而,现实中许多学生却因资料庞杂、逻辑混乱或时间不足,难以写出一篇真正“有据、有理、有深度”…...

电子制造工厂场景,AI自动化方案主流厂商横评:2026年智慧工厂选型深度解析

站在2026年的时间节点回看,电子制造工厂的数字化转型已完成从“单点自动化”向“系统智能化”的跨越。 随着全球供应链波动的常态化,AI自动化方案已不再是锦上添花的“实验室项目”, 而是关乎企业在0.1毫米精度竞争中能否生存的底层基座。 根…...

免费开源乐谱识别工具Audiveris:从纸质乐谱到数字音乐的三步转换指南

免费开源乐谱识别工具Audiveris:从纸质乐谱到数字音乐的三步转换指南 【免费下载链接】audiveris Latest generation of Audiveris OMR engine 项目地址: https://gitcode.com/gh_mirrors/au/audiveris 还在为整理成堆的纸质乐谱而烦恼吗?Audiver…...

思源宋体TTF:5分钟掌握免费商用中文字体的完整使用指南

思源宋体TTF:5分钟掌握免费商用中文字体的完整使用指南 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 如果你正在寻找一款既专业又免费的中文字体,那么思源宋体…...

BBDown:专业高效的哔哩哔哩命令行下载器完全指南

BBDown:专业高效的哔哩哔哩命令行下载器完全指南 【免费下载链接】BBDown Bilibili Downloader. 一个命令行式哔哩哔哩下载器. 项目地址: https://gitcode.com/gh_mirrors/bb/BBDown 在当今数字内容消费时代,高效获取和管理在线视频资源已成为许多…...

PotPlayer字幕翻译插件终极指南:3步实现跨语言视频无障碍观看

PotPlayer字幕翻译插件终极指南:3步实现跨语言视频无障碍观看 【免费下载链接】PotPlayer_Subtitle_Translate_Baidu PotPlayer 字幕在线翻译插件 - 百度平台 项目地址: https://gitcode.com/gh_mirrors/po/PotPlayer_Subtitle_Translate_Baidu 还在为外语视…...

STM32串口高效通信秘籍:巧用DMA+空闲中断实现不定长数据收发(基于CubeIDE)

STM32串口高效通信秘籍:巧用DMA空闲中断实现不定长数据收发(基于CubeIDE) 在物联网设备和嵌入式系统开发中,串口通信是最基础也最关键的通信方式之一。无论是传感器数据采集、设备间通信还是与上位机交互,稳定高效的串…...

Keil MDK 项目迁移避坑指南:当你的旧工程遇到‘Default Compiler Version 5 is not available’

Keil MDK项目迁移实战:编译器版本冲突的工程级解决方案 当你从同事手中接过一个历史遗留的Keil MDK项目,或从版本控制系统拉取多年前的嵌入式工程时,最令人头疼的莫过于打开工程后迎面而来的编译器报错。其中"Default Compiler Version …...

嵌入式网络硬件设计避坑指南:如何为你的SOC选配合适的PHY芯片与接口(MII/RMII实战解析)

嵌入式网络硬件设计避坑指南:如何为你的SOC选配合适的PHY芯片与接口(MII/RMII实战解析) 在嵌入式系统设计中,网络功能已成为现代智能设备的标配需求。无论是工业控制、物联网终端还是消费电子产品,稳定可靠的网络连接往…...

告别卡顿!用Sunshine打造私人游戏串流服务器的完整指南

告别卡顿!用Sunshine打造私人游戏串流服务器的完整指南 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 你是否曾经梦想过在任何设备上流畅玩PC游戏?无论是躺…...

在华为欧拉openEuler 22.03 SP2上搞定Oracle 11g R2:一个踩坑无数的可视化安装实录

在华为欧拉openEuler 22.03 SP2上搞定Oracle 11g R2:一个踩坑无数的可视化安装实录 当国产操作系统遇上传统商业数据库,这场跨越技术栈的"联姻"注定充满挑战。作为在openEuler 22.03 SP2上成功部署Oracle 11g R2的实践者,我将以时间…...

不只是定位:教你用开源GNSS/INS平台玩转多传感器融合与抗干扰

不只是定位:开源GNSS/INS平台的多传感器融合与抗干扰实战指南 在自动驾驶、无人机和机器人领域,精准的定位与导航系统是核心竞争力的体现。传统单一GNSS系统在城市峡谷、电磁干扰等复杂环境下表现往往不尽如人意,而单纯依赖惯性导航系统(INS)…...

如何用3分钟完成淘宝淘金币全任务?终极自动化脚本完全指南

如何用3分钟完成淘宝淘金币全任务?终极自动化脚本完全指南 【免费下载链接】taojinbi 淘宝淘金币自动执行脚本,包含蚂蚁森林收取能量,芭芭农场全任务,解放你的双手 项目地址: https://gitcode.com/gh_mirrors/ta/taojinbi …...

如何用Python自动化脚本轻松抢到大麦网演唱会门票:终极指南

如何用Python自动化脚本轻松抢到大麦网演唱会门票:终极指南 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 还在为抢不到心仪演唱会门票而烦恼吗?当周杰伦、五月天等热门演…...

告别键盘连击烦恼:KeyboardChatterBlocker 智能解决方案详解

告别键盘连击烦恼:KeyboardChatterBlocker 智能解决方案详解 【免费下载链接】KeyboardChatterBlocker A handy quick tool for blocking mechanical keyboard chatter. 项目地址: https://gitcode.com/gh_mirrors/ke/KeyboardChatterBlocker 你是否曾经在打…...

N_m3u8DL-RE完整教程:跨平台流媒体下载的终极解决方案

N_m3u8DL-RE完整教程:跨平台流媒体下载的终极解决方案 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-RE …...

百度千帆平台文心4.0 API开通与计费避坑指南:个人开发者如何低成本尝鲜?

百度千帆文心4.0 API低成本接入实战:个人开发者的精打细算指南 当大模型API成为个人开发者的"新基建",如何用最低成本体验最前沿的ERNIE-Bot-4能力?作为经历过完整踩坑流程的实践者,我将分享从申请到调用的全流程避坑策…...

告别AWCC臃肿:500KB轻量级Alienware灯光风扇控制终极方案

告别AWCC臃肿:500KB轻量级Alienware灯光风扇控制终极方案 【免费下载链接】alienfx-tools Alienware systems lights, fans, and power control tools and apps 项目地址: https://gitcode.com/gh_mirrors/al/alienfx-tools 厌倦了Alienware Command Center&…...

在野火征途Pro上跑RT-Thread:手把手教你为自研RISC-V SoC移植操作系统

在野火征途Pro上跑RT-Thread:手把手教你为自研RISC-V SoC移植操作系统 当一块裸板首次点亮LED时,那种成就感就像电子工程师的"Hello World"。但要让这块板子真正活起来,操作系统是不可或缺的灵魂。本文将带你从零开始,在…...

Nginx、Tengine、OpenRestry的http和tcp后端健康检查【20260520-005篇】

文章目录 ✅ 一、核心能力概览(按产品维度) ✅ 二、HTTP 健康检查配置示例(三者对比) ▪️ Nginx(被动式,基础可靠) ▪️ Tengine(主动式,开箱即用) ▪️ OpenResty(Lua 主动式,高度可控) ✅ 三、TCP 健康检查配置示例 ▪️ Tengine(最简洁) ▪️ OpenResty(TC…...

逆向实战:用WT-JS_DEBUG_V1.8.3快速定位并导出AES加密参数到Python

逆向工程实战:从浏览器到Python的AES加密参数高效迁移指南 在数据采集和接口分析领域,遇到前端加密是再常见不过的挑战。特别是当网站采用AES加密时,如何快速提取关键参数并复用到Python脚本中,成为许多开发者头疼的问题。本文将…...

告别桌面混乱!用Utools的「本地文件启动」功能,5分钟打造你的专属文件启动器

告别桌面混乱!用Utools的「本地文件启动」功能,5分钟打造你的专属文件启动器 每次打开电脑,看到满屏的文件图标和杂乱无章的文件夹,是不是感觉工作效率瞬间降了一半?作为一名长期与文件打交道的专业人士,我…...

Nginx、Tengine、OpenRestry的http和tcp后端健康检查【20260520-004篇】

文章目录 企业级生产环境 Nginx/Tengine/OpenResty 健康检查 完整部署+配置+压测+故障演练+验收交付文档 一、环境基线与生产规范 1. 版本选型(生产强制) 2. 生产统一参数规范(全局通用) 3. 生产前置约束 二、三大组件 生产完整配置 2.1 开源Nginx 生产配置(仅被动检查,无…...