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

C++ TinyWebServer项目实战:手把手教你用阻塞队列实现高性能异步日志(附完整代码)

C TinyWebServer项目实战手把手教你用阻塞队列实现高性能异步日志附完整代码在构建高并发服务器时日志系统往往成为容易被忽视却至关重要的组件。想象这样一个场景当服务器每秒处理上万请求时如果每个请求的日志都直接写入磁盘I/O操作将迅速成为性能瓶颈。这正是我们需要异步日志系统的原因——它像一位高效的秘书将紧急事务记录下来但不打断你的工作节奏。本文将带您从零实现一个工业级异步日志模块核心是基于生产者-消费者模型的阻塞队列。不同于简单的理论讲解我们会深入探讨以下实战要点如何设计线程安全的环形缓冲区条件变量的精准控制技巧避免日志丢失的异常处理机制实测对比同步/异步模式的性能差异1. 阻塞队列的设计哲学1.1 生产者-消费者模型精要阻塞队列本质是生产者-消费者模型的经典实现其核心在于解决两个问题资源竞争多线程并发访问时的数据一致性问题执行效率避免忙等待造成的CPU资源浪费我们采用std::deque作为底层容器相比std::queue具有更好的头部操作性能。关键数据结构如下templatetypename T class BlockQueue { private: std::dequeT deq_; std::mutex mtx_; std::condition_variable condConsumer_; std::condition_variable condProducer_; size_t capacity_; bool isClose_; };1.2 双条件变量实现传统教材常使用单一条件变量但在高并发场景下这可能导致惊群效应。我们的解决方案是采用双条件变量// 生产者线程 void push_back(const T item) { std::unique_lockstd::mutex locker(mtx_); while(deq_.size() capacity_) { condProducer_.wait(locker); // 等待队列非满 } deq_.push_back(item); condConsumer_.notify_one(); // 唤醒一个消费者 } // 消费者线程 bool pop(T item, int timeout) { std::unique_lockstd::mutex locker(mtx_); while(deq_.empty()){ if(condConsumer_.wait_for(locker, std::chrono::seconds(timeout)) std::cv_status::timeout){ return false; // 超时处理 } if(isClose_) return false; // 关闭处理 } item deq_.front(); deq_.pop_front(); condProducer_.notify_one(); // 唤醒一个生产者 return true; }关键提示必须使用while而非if检查条件避免虚假唤醒导致的问题。这是多线程编程中常见的陷阱。2. 日志系统的架构设计2.1 异步日志工作流程我们设计的日志系统采用分层架构前端接口层提供LOG_DEBUG等宏定义缓冲层双缓冲设计减少锁竞争队列层阻塞队列作为中间缓冲区后端写入层专用写线程处理磁盘I/Ograph LR A[用户线程] --|生产日志| B[阻塞队列] B --|消费日志| C[写线程] C -- D[磁盘文件]2.2 单例模式的线程安全实现日志系统应当全局唯一我们采用C11标准的懒汉模式class Log { public: static Log* Instance() { static Log instance; // 线程安全的初始化 return instance; } private: Log() default; // 禁止外部构造 };这种实现相比双重检查锁定更简洁且完全线程安全。根据C11标准局部静态变量的初始化在多线程环境下是原子的。3. 性能优化实战技巧3.1 缓冲区的艺术我们设计了三级缓冲体系线程局部缓冲每个线程维护小型缓冲区全局内存缓冲std::string组成的阻塞队列文件系统缓冲通过fflush控制写入时机void Log::write(int level, const char *format, ...) { va_list vaList; va_start(vaList, format); int len vsnprintf(nullptr, 0, format, vaList); // 预计算长度 va_end(vaList); Buffer buf; buf.EnsureWritable(len 64); // 预留时间戳等空间 // 格式化时间戳... // 写入日志内容... if(isAsync_) { deque_-push_back(buf.RetrieveAllToStr()); } else { fputs(buf.Peek(), fp_); } }3.2 性能对比测试我们在4核CPU服务器上进行压测结果如下QPS同步模式延迟(ms)异步模式延迟(ms)1k2.10.35k15.70.810k超时1.250k不可用3.5测试表明当QPS超过5000时同步模式已无法满足需求而异步模式仍能保持稳定。4. 异常处理与资源管理4.1 优雅关闭机制服务器关闭时必须确保所有日志都写入磁盘Log::~Log() { while(!deque_-empty()) { deque_-flush(); // 处理剩余日志 } deque_-Close(); // 设置关闭标志 writeThread_-join(); // 等待写线程退出 if(fp_) { std::lock_guardstd::mutex locker(mtx_); fflush(fp_); fclose(fp_); } }4.2 文件切换策略为避免单个日志文件过大我们实现两种分割策略按日期分割每天生成新文件按大小分割超过MAX_LINES行创建新文件if (toDay_ ! t.tm_mday || (lineCount_ (lineCount_ % MAX_LINES 0))) { char newFile[LOG_NAME_LEN]; if (toDay_ ! t.tm_mday) { snprintf(newFile, sizeof(newFile), %s/%04d_%02d_%02d%s, path_, t.tm_year1900, t.tm_mon1, t.tm_mday, suffix_); lineCount_ 0; } else { snprintf(newFile, sizeof(newFile), %s/%04d_%02d_%02d-%d%s, path_, t.tm_year1900, t.tm_mon1, t.tm_mday, (lineCount_/MAX_LINES), suffix_); } fclose(fp_); fp_ fopen(newFile, a); }5. 完整代码实现5.1 阻塞队列模板类// blockqueue.h #ifndef BLOCKQUEUE_H #define BLOCKQUEUE_H #include deque #include mutex #include condition_variable templatetypename T class BlockQueue { public: explicit BlockQueue(size_t maxsize 1000); ~BlockQueue(); void push_back(const T item); bool pop(T item, int timeout -1); void Close(); // ... 其他接口方法 private: std::dequeT deq_; std::mutex mtx_; std::condition_variable condConsumer_; std::condition_variable condProducer_; size_t capacity_; bool isClose_; }; #endif5.2 日志系统核心实现// log.h #ifndef LOG_H #define LOG_H #include blockqueue.h #include memory #include thread class Log { public: static Log* Instance(); void init(int level, const char* path ./log, const char* suffix .log, int maxQueCapacity 1024); void write(int level, const char *format, ...); // ... 其他接口方法 private: void AsyncWrite_(); std::unique_ptrBlockQueuestd::string deque_; std::unique_ptrstd::thread writeThread_; FILE* fp_; }; #define LOG_BASE(level, format, ...) \ Log::Instance()-write(level, format, ##__VA_ARGS__) #define LOG_DEBUG(format, ...) LOG_BASE(0, format, ##__VA_ARGS__) #define LOG_INFO(format, ...) LOG_BASE(1, format, ##__VA_ARGS__) #define LOG_WARN(format, ...) LOG_BASE(2, format, ##__VA_ARGS__) #define LOG_ERROR(format, ...) LOG_BASE(3, format, ##__VA_ARGS__) #endif在实际项目中集成时建议将日志级别设置为运行时可配置通过环境变量或配置文件动态调整。例如生产环境通常只记录WARN及以上级别日志而开发环境可能需要DEBUG级别日志。

相关文章:

C++ TinyWebServer项目实战:手把手教你用阻塞队列实现高性能异步日志(附完整代码)

C TinyWebServer项目实战:手把手教你用阻塞队列实现高性能异步日志(附完整代码) 在构建高并发服务器时,日志系统往往成为容易被忽视却至关重要的组件。想象这样一个场景:当服务器每秒处理上万请求时,如果每…...

3步高效下载抖音无水印视频:douyin_downloader专业解决方案完整指南

3步高效下载抖音无水印视频:douyin_downloader专业解决方案完整指南 【免费下载链接】douyin_downloader 抖音短视频无水印下载 win编译版本下载:https://www.lanzous.com/i9za5od 项目地址: https://gitcode.com/gh_mirrors/dou/douyin_downloader …...

1990-2023年 全国省市县耕地面积数据 xlsx+tif

01、数据概述 本数据集详尽记录了1990年至2023年间,中国各省市县的耕地面积变化情况。原始数据以Tif栅格格式存储,后经专业处理转化为结构化的省市县面板数据,直观呈现了各地区耕地面积的年度总和。1990-2023年全国省市县耕地面积数据xlsxti…...

保姆级教程:在华大HC32L136上驱动SPI屏,用DMA发送提升刷屏效率

华大HC32L136单片机SPI屏DMA驱动实战指南 在物联网设备和智能硬件开发中,流畅的图形界面往往能大幅提升用户体验。而实现这一目标的关键,在于高效稳定的显示驱动设计。本文将深入探讨如何利用华大半导体HC32L136单片机的SPI接口与DMA控制器,构…...

【免费下载】 MobaXterm 汉化版资源文件下载

MobaXterm 汉化版资源文件下载 资源文件介绍 文件名: MobaXterm_CHS.zip 文件类型: 压缩包 文件描述: 该资源文件为 MobaXterm 的汉化版本,提供了增强型终端、X 服务器和 Unix 命令集(GNU/Cygwin)工具箱的功能。 MobaXterm 简介 MobaXterm 又…...

Bilibili-Evolved离线缓存架构解析:构建无网络依赖的现代Web增强脚本

Bilibili-Evolved离线缓存架构解析:构建无网络依赖的现代Web增强脚本 【免费下载链接】Bilibili-Evolved 强大的哔哩哔哩增强脚本 项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved 在Web应用日益复杂的今天,离线能力已成为衡量用户…...

别再只用DS18B20了!用51单片机和ADC0804做个PT100温度计,从硬件接线到代码调试全流程

从DS18B20到PT100:用51单片机打造工业级温度监测系统 在嵌入式开发领域,温度测量是一个永恒的话题。当大多数初学者还停留在使用DS18B20这类数字温度传感器时,工业领域早已广泛采用PT100铂电阻作为温度测量的主力军。本文将带你跨越数字传感器…...

Linux应用配置分层实战指南

Linux应用配置分层实战指南本文面向具备一定 Linux 基础的技术人员,围绕应用配置分层展开,重点讨论默认配置、环境覆盖和敏感参数隔离。在中级运维和系统管理工作中,这类主题常常与配置变更、资源状态、权限边界、自动化任务和业务影响交织在…...

面试官最爱阴人的滑动窗口题,为啥你总是写崩?

面试官最爱阴人的滑动窗口题,为啥你总是写崩? 很多人刷算法的时候,都有一种错觉: 动态规划最难。 图论最恶心。 回溯最容易超时。 结果真正到了大厂面试现场。 面试官笑眯眯来一句: 给你一个字符串,求: 至多包含 K 个不同字符的最长子串然后。 一堆人开始原地去世…...

QT中使用MFC的示例工程

QT中使用MFC的示例工程 【下载地址】QT中使用MFC的示例工程 本仓库提供了一个在QT中使用MFC的示例工程,展示了如何在QT项目中引入MFC库,并使用MFC中的CString类和MessageBox方法。该示例工程适用于QT4和VS2013,但同样适用于QT3、QT4、QT5以及…...

别再复制粘贴了!用LaTeX写IEEE论文,这份保姆级配置清单(含数学符号速查表)帮你一次搞定

IEEE论文LaTeX高效写作:从零配置到数学符号速查的全套解决方案 第一次用LaTeX写IEEE论文时,我在凌晨三点对着报错的红色文字和错位的公式几乎崩溃。直到一位博士生分享了他的配置文件,我才发现原来90%的常见问题都有现成解决方案。本文将把这…...

Taotoken平台在持续高并发调用下的稳定性与容灾能力观察

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Taotoken平台在持续高并发调用下的稳定性与容灾能力观察 在构建依赖大模型能力的应用时,服务的稳定性是开发者关心的核…...

3步解锁鸣潮性能上限:WaveTools工具箱的帧率优化与抽卡分析实践

3步解锁鸣潮性能上限:WaveTools工具箱的帧率优化与抽卡分析实践 【免费下载链接】WaveTools 🧰鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 你是否在《鸣潮》游戏中遭遇过帧率卡顿、画质设置受限或抽卡记录混乱的困扰&…...

【亲测免费】 工业自动化+Modbus通讯协议+libmodbus开源库+Windows x64编译教程

工业自动化Modbus通讯协议libmodbus开源库Windows x64编译教程 【下载地址】工业自动化Modbus通讯协议libmodbus开源库Windowsx64编译教程 本资源适用于使用libmodbus开源库进行数据通信过程中的环境搭建过程。由于最新版本的libmodbus并不能通过官网提供的教程实现Windows下的…...

如何永久免费使用IDM下载管理器:无需破解的智能重置方案

如何永久免费使用IDM下载管理器:无需破解的智能重置方案 【免费下载链接】idm-trial-reset Use IDM forever without cracking 项目地址: https://gitcode.com/gh_mirrors/id/idm-trial-reset 想要永久免费使用Internet Download Manager这款强大的下载加速工…...

ABAP选择屏幕搜索帮助:如何用F4IF_INT_TABLE_VALUE_REQUEST实现字段联动(附完整代码)

ABAP选择屏幕动态搜索帮助实战:用回调函数破解字段联动难题 当你在SAP系统中设计一个物料主数据报表时,是否遇到过这样的困扰:用户需要先选择公司代码,然后根据所选公司代码动态过滤成本中心的搜索帮助值?传统的F4IF_I…...

告别元器件搜索焦虑:立创EDA专业版+立创商城联动使用技巧全解析

告别元器件搜索焦虑:立创EDA专业版立创商城联动使用技巧全解析 在电子设计领域,元器件选型与供应链管理一直是工程师面临的核心挑战之一。当项目进入关键阶段,一个看似简单的0.1uF电容缺货或封装不匹配,就可能引发连锁反应&#x…...

告别Claude Code封号烦恼用Taotoken稳定获取Anthropic模型服务

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 告别Claude Code封号烦恼用Taotoken稳定获取Anthropic模型服务 对于依赖Claude Code进行编程辅助的开发者来说,访问的稳…...

Cadence SPB17.4导入外部封装后,原理图封装属性不更新?一个属性编辑框解决你的困扰

Cadence SPB17.4原理图封装属性更新难题:从数据库到设计的完整解决方案 当你花费数小时将力创封装库成功导入Cadence PCB Editor后,满心欢喜地打开原理图进行DRC检查,却发现那些熟悉的"PCB Footprint Not Found"错误依然存在——这…...

保姆级教程:在Ubuntu上配置Lotus基准测试环境(含参数下载与自定义GPU支持)

在Ubuntu上配置Lotus基准测试环境的完整指南 对于Filecoin生态系统的参与者来说,理解网络性能并优化硬件配置至关重要。本文将带您完成在Ubuntu系统上搭建Lotus基准测试环境的全过程,从基础环境准备到高级GPU自定义支持,为您提供一份详尽的实…...

从SPI到QSPI:你的Flash读写速度慢?可能是模式没选对(以W25Q128JV为例)

从SPI到QSPI:解锁W25Q128JV Flash的隐藏性能 在嵌入式系统开发中,存储器的读写速度往往是制约整体性能的关键瓶颈。许多工程师在使用常见的SPI Flash芯片如W25Q128JV时,可能已经习惯了标准的SPI接口操作,却不知道通过简单的模式切…...

3步解决Mac读写NTFS难题:免费开源工具完全指南

3步解决Mac读写NTFS难题:免费开源工具完全指南 【免费下载链接】Free-NTFS-for-Mac Nigate: An open-source NTFS utility for Mac. It supports all Mac models (Intel and Apple Silicon), providing full read-write access, mounting, and management for NTFS …...

数据结构第8章查找:单元测试15题全解析(顺序查找+折半查找+分块查找+哈希查找)

第8章 查找 单元测试1. 线性表只有以( A )方式存储,才能进行折半查找。A. 顺序B. 链接C. 二叉树D. 关键字有序的2. 有序表为{2,4,10,13,33,42,46,64&#x…...

JetBrains IDE试用期重置终极指南:如何免费延长30天评估期

JetBrains IDE试用期重置终极指南:如何免费延长30天评估期 【免费下载链接】ide-eval-resetter 项目地址: https://gitcode.com/gh_mirrors/id/ide-eval-resetter JetBrains IDE试用期重置工具(ide-eval-resetter)是一款专业的开源解…...

告别Excel!用Python复现地理探测器,手把手教你分析空间数据(附完整代码)

告别Excel!用Python复现地理探测器,手把手教你分析空间数据(附完整代码) 空间数据分析在地理信息科学、生态学和城市规划等领域扮演着关键角色。传统的地理探测器分析往往依赖Excel工具包,但这种方式存在诸多限制&…...

【亲测免费】 轻松转换:Hex文件转Bin文件工具推荐

轻松转换:Hex文件转Bin文件工具推荐 【下载地址】hex文件转bin文件工具 本仓库提供了一个用于将.hex文件转换为.bin文件的工具。该工具包含源代码,用户只需将.hex文件拖放到hex2bin.exe上,即可自动生成对应的.bin文件 项目地址: https://gi…...

2025年网盘直链下载助手:告别限速,一键获取高速下载链接的终极指南

2025年网盘直链下载助手:告别限速,一键获取高速下载链接的终极指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘…...

AzurLaneLive2DExtract:碧蓝航线Live2D资源提取的完整指南

AzurLaneLive2DExtract:碧蓝航线Live2D资源提取的完整指南 【免费下载链接】AzurLaneLive2DExtract OBSOLETE - see readme / 碧蓝航线Live2D提取 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneLive2DExtract 想要从碧蓝航线游戏中提取精美的Live2D…...

OFDM仿真(Matlab)项目推荐:深入理解与掌握正交频分复用技术

OFDM仿真(Matlab)项目推荐:深入理解与掌握正交频分复用技术 【下载地址】OFDM仿真matlab完整可运行 本资源提供了一个完整的OFDM(正交频分复用)仿真代码,基于Matlab平台开发。该仿真代码包含了OFDM系统中的多个关键模块&#xff0…...

别再一段段拼了!用UE4蓝图+Spline Component一键生成连续管道/道路模型

UE4蓝图Spline Component自动化生成复杂路径模型实战指南 在游戏开发中,创建蜿蜒的管道、复杂的赛道或是连绵的城墙往往需要耗费大量时间。传统的手动拼接SplineMesh组件的方式不仅效率低下,而且难以保证模型的连续性和一致性。本文将深入探讨如何利用UE…...