详细介绍:异步日志系统

news/2025/10/4 9:07:54/文章来源:https://www.cnblogs.com/tlnshuju/p/19125294

详细介绍:异步日志系统

2025-10-04 09:03  tlnshuju  阅读(0)  评论(0)    收藏  举报

AsynLogging类,核心功能是通过后台线程异步写入日志,避免前端线程因 IO 操作阻塞,提升系统性能。

一、核心设计思路

异步日志的核心是 “前端生产日志,后端消费日志”的分离模式:

  • 前端线程(业务线程)通过append方法将日志写入内存缓冲区,操作高效(无磁盘 IO)。
  • 后端线程(日志线程)定期或被唤醒后,将缓冲区中的日志批量写入磁盘文件。
2. 成员变量(注释中声明的核心变量)
变量名作用
flushInterval_日志刷新间隔(如 3 秒,后端线程定期刷新)
running_原子变量,标记日志系统是否运行(线程安全的状态控制)
basename_日志文件基础名称(用于生成滚动日志文件名)
rollSize_日志文件滚动阈值(超过该大小则创建新文件,复用LogFile的滚动逻辑)
pthread_后台日志线程的智能指针
mutex_ + cond_互斥锁 + 条件变量,用于前端与后端线程的同步
currentBuffer_前端当前使用的日志缓冲区(字符串形式)
buffers_已填满的缓冲区队列(等待后端线程处理)
output_LogFile对象,负责实际的日志文件写入和滚动管理
latch_倒计时门闩(用于确保后台线程启动完成后,前端才开始写入日志)

AsynLogging::append 方法中判断是否需要切换当前日志缓冲区的核心条件,用于决定是否将当前缓冲区(currentBuffer_)移入待处理队列并创建新缓冲区,具体解析如下:

条件逻辑

if (currentBuffer_.size() >= BufMaxLen ||currentBuffer_.capacity() - currentBuffer_.size() < len)
  • 两个判断条件用 ||(逻辑或)连接,满足任意一个即触发缓冲区切换。

条件 1:currentBuffer_.size() >= BufMaxLen

  • 含义:当前缓冲区的已使用大小(size())大于等于预设的最大容量(BufMaxLen = 4KB)。
  • 作用:确保单个缓冲区不会无限增长,当达到上限时,强制将其移入待处理队列(buffers_),由后端线程写入磁盘。

条件 2:currentBuffer_.capacity() - currentBuffer_.size() < len

  • 含义:当前缓冲区的剩余可用空间(总容量 capacity() 减去已使用大小 size())小于本次要写入的日志长度(len)。
  • 作用:避免因 “剩余空间不足” 导致日志被截断或缓冲区被迫扩容。即使缓冲区未填满(未达 BufMaxLen),但剩余空间不够写入当前日志时,也会提前切换缓冲区,保证日志完整性。

触发后的操作

当上述条件满足时,代码会执行:

buffers_.push_back(std::move(currentBuffer_));  // 将当前缓冲区移入待处理队列
currentBuffer_.reserve(BufMaxLen);              // 重置新缓冲区,预留最大容量
  • 通过 std::move 转移当前缓冲区的所有权到队列,避免拷贝开销。
  • 新缓冲区预分配 BufMaxLen 大小的空间,减少后续写入时的内存分配次数。

设计目的

  1. 保证日志完整性:避免因空间不足导致日志片段化或丢失。
  2. 控制内存占用:单个缓冲区大小不超过 4KB,防止内存过度消耗。
  3. 减少 IO 次数:缓冲区满或空间不足时才切换,实现批量写入,提升效率。
void AsynLogging::append(const char *msg, const size_t len)
{std::unique_lock lock(mutex_);  // 加锁保证线程安全// 若当前缓冲区不足(已满或剩余空间不够),则将其移入队列,换新缓冲区if (currentBuffer_.size() >= BufMaxLen ||currentBuffer_.capacity() - currentBuffer_.size() < len){buffers_.push_back(std::move(currentBuffer_));  // 转移当前缓冲区所有权currentBuffer_.reserve(BufMaxLen);              // 重置新缓冲区}currentBuffer_.append(msg, len);  // 写入日志到当前缓冲区cond_.notify_all();               // 唤醒后端线程处理缓冲区
}
  • 核心作用:前端线程写入日志到内存缓冲区,避免直接写磁盘。
  • 线程安全:通过mutex_加锁,支持多线程并发写入。
  • 缓冲区切换:当当前缓冲区不足时,将其移入待处理队列,创建新缓冲区继续写入。
  • 唤醒后端:写入后通知后端线程有数据待处理。

倒计时门闩(CountDownLatch) 类

用于多线程同步场景,核心功能是等待一个或多个线程完成特定操作后,再继续执行当前线程。

一、类的核心成员(注释中声明)

  • count_:倒计时计数器,初始化为指定值,每调用一次countDown()减 1,直至为 0。
  • mutex_:互斥锁,用于保护count_的线程安全访问。
  • cond_:条件变量,用于线程间的等待与唤醒机制。

二、核心方法解析

1. 构造函数 CountDownLatch(int count)
CountDownLatch::CountDownLatch(int count) : count_(count) {}
  • 初始化count_为传入的计数初始值(例如,若需要等待 3 个线程完成,则初始化为 3)。
2. wait() 方法
void CountDownLatch::wait()
{std::unique_lock lock(mutex_);  // 加锁,确保线程安全while(count_ > 0)  // 循环判断,避免虚假唤醒{cond_.wait(lock);  // 释放锁并阻塞等待,被唤醒时重新获取锁}
}
  • 作用:调用该方法的线程会阻塞,直到count_减为 0 才继续执行。
  • 线程安全:通过std::unique_lock加锁,保证对count_的访问互斥。
  • 防止虚假唤醒:使用while循环而非if判断,确保被唤醒后再次检查count_是否真的为 0(条件变量可能因系统原因虚假唤醒)。
3. countDown() 方法
void CountDownLatch::countDown()
{std::unique_lock lock(mutex_);  // 加锁count_ -= 1;  // 计数器减1if(count_ == 0)  // 当计数器归0时{cond_.notify_all();  // 唤醒所有等待的线程}
}
  • 作用:每调用一次,计数器count_减 1;当count_变为 0 时,唤醒所有通过wait()阻塞的线程。
  • 线程安全:加锁确保count_的修改是原子操作,避免多线程并发修改导致的计数错误。
4. getCount() 方法
int CountDownLatch::getCount() const
{std::unique_lock lock(mutex_);  // 加锁return count_;  // 返回当前计数器值
}
  • 作用:获取当前count_的数值(线程安全的访问)。

三、典型使用场景

倒计时门闩常用于以下同步场景:

  1. 主线程等待子线程初始化:例如,主线程启动 N 个子线程后,调用wait()阻塞,每个子线程初始化完成后调用countDown(),当所有子线程初始化完毕(count_归 0),主线程被唤醒继续执行。
  2. 协调多个线程完成任务:例如,多个线程完成各自任务后调用countDown(),最后一个线程完成时唤醒等待的线程进行汇总操作。

四、核心设计思想

  • 线程同步:通过互斥锁(mutex_)保护共享变量count_,通过条件变量(cond_)实现线程间的等待 / 唤醒。
  • 计数器机制:用count_跟踪待完成的操作数量,归 0 时触发同步点。
  • 安全性:避免了多线程并发修改计数器的竞态条件,且通过循环判断防止条件变量的虚假唤醒。

3. 为什么需要reserve?

3.1 移动后的状态不确定性

std::vector currentBuffer_(BufMaxLen);
// ... 填充数据
// 移动后状态不确定
buffers_.push_back(std::move(currentBuffer_));
// 此时 currentBuffer_ 可能是:
// 情况1: size=0, capacity=0 (需要重新分配)
// 情况2: size=0, capacity=BufMaxLen (理想情况)
// 情况3: 其他未指定状态
// 为保证一致性,显式reserve
currentBuffer_.reserve(BufMaxLen);

std::move后原来的空间确实可能丢失,这正是需要reserve的关键原因。

1. std::move后的不确定性

标准规定:

  • 被移动后的对象处于有效但未指定状态

  • 实现可以自由选择如何处置被移动的对象

实际可能的情况:

std::vector currentBuffer_(1000); // 容量1000
// 移动操作后,currentBuffer_ 可能:
auto movedBuffer = std::move(currentBuffer_);
// 情况1: 容量清零(常见实现)
// currentBuffer_.capacity() == 0
// 情况2: 容量保留(某些优化)
// currentBuffer_.capacity() == 1000
// 情况3: 其他任意有效状态

2. 具体验证代码

#include 
#include 
void demonstrateMoveUncertainty() {std::vector buffer(1000, 42); // 容量1000std::cout << "移动前 - Size: " << buffer.size()<< ", Capacity: " << buffer.capacity() << std::endl;std::vector newBuffer = std::move(buffer);std::cout << "移动后 - Size: " << buffer.size()<< ", Capacity: " << buffer.capacity() << std::endl;// 不同编译器的可能输出:// GCC:   移动后 - Size: 0, Capacity: 0// Clang: 移动后 - Size: 0, Capacity: 0// MSVC:  移动后 - Size: 0, Capacity: 1000 (可能保留)
}

3. 为什么空间会丢失?

移动语义的实现选择:

// vector移动构造函数的可能实现之一
vector(vector&& other) noexcept: size_(other.size_), capacity_(other.capacity_), data_(other.data_)
{// 标准允许:可以清零原对象other.size_ = 0;other.capacity_ = 0;  // 这里可能清零容量!other.data_ = nullptr;
}
// 或者另一种实现:
vector(vector&& other) noexcept: size_(other.size_), capacity_(other.capacity_), data_(other.data_)
{// 也可能保留原对象的容量other.size_ = 0;// other.capacity_ 保持不变other.data_ = nullptr;
}

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

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

相关文章

Linux基础开发工具 --- vim - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025十一集训——Day2模拟赛

赛时: 四个题都很可做的样子, T3好像是原??? OK T1 简单二分,切了。 调 lowerbound 那里 +-1 的细节,8:50 成功过掉大样例。 开 T2。 9:00,想到差分。 诶T2咋是原,就一黄f**k.本文来自博客园,作者:zhangxia…

完整教程:ARM Cortex-M:内存保护单元 (MPU) 发布

完整教程:ARM Cortex-M:内存保护单元 (MPU) 发布pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…

【Clion】【文件编码】Clion内置控制台中文字体乱码的解决方案及编码格式调整

View Post【Clion】【文件编码】Clion内置控制台中文字体乱码的解决方案及编码格式调整Clion内置控制台中文字体乱码的解决方案及编码格式调整一、问题描述 在使用Clion时,在保证文件编码,项目编码,属性文件的默认编…

完整教程:JavaWeb零基础学习Day1——HTMLCSS

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

淘宝联盟网站建设源码网站服务器租赁合同

疑问&#xff1a;Mysql多事务默认情况下&#xff0c;同时修改同一条记录运行修改吗&#xff1f;是否要手动加上for update行锁。 猜想&#xff1a;MySQL 会自动对涉及的数据行加上写锁&#xff08;排他锁&#xff09;&#xff0c;以确保数据的一致性和隔离性。这是在默认的事务…

2025十一集训——Day模拟赛

赛时: 四个题都很可做的样子, T3好像是原??? OK T1 简单二分,切了。 调 lowerbound 那里 +-1 的细节,8:50 成功过掉大样例。 开 T2。本文来自博客园,作者:zhangxiao666,转载请注明原文链接:https://www.cnb…

Qt纯代码实现智能安防集中管理平台/楼宇对讲管理系统/门禁管理/视频监控

一、前言说明 这个项目很多年前就完成的,属于一个定制的项目,最初做的事楼宇对讲相关的功能,后面陆续增加了门禁和视频监控,这些模块加起来,慢慢的形成了一个智能安防集中管理平台的模样,但是确切的说又不够标准…

在织梦网站做静态网页伦敦做网站

文章目录 一、Hazelcast简介1、Hazelcast概述2、Hazelcast之IMDG3、数据分区 二、Hazelcast配置1、maven坐标2、集群搭建&#xff08;1&#xff09;组播自动搭建 3、客户端4、集群分组5、其他配置 三、Hazelcast分布式数据结构1、IMap2、IQueue&#xff1a;队列3、MultiMap4、I…

长沙做网站推广代理记账公司注册需要什么条件

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 人工智能&#xff08;AI&#xff09;已经成为当今世界最具革命性的技术之一&#xff0c;它正在深刻改变各个行业&#x…

网站流量提升方法vps搭建asp网站

八爪鱼,被动收入,财务自由,现金流,现金流游戏,各银行利率,money,资产负债表,财务自由,资产管理,个人理财,管理个人资产,理财,打造被动收入,躺着赚钱,让钱为我打工

汉文博士词典库源文件已在 github 开放

无版权问题的词典源文件已从城通网盘转存至 Github: https://github.com/wmjordan/Hanbox.Dict 部分词典的编译配置文件需使用汉文博士 7.0 或更新的版本编译。

网站自己怎么制作做网站用asp还是php

3.1 表达式和语句 表达式一共分为三种&#xff1a; &#xff08;1&#xff09;变量或常量 运算符构成的计算表达式 &#xff08;2&#xff09;new 表达式&#xff0c;结果是一个数组或类的对象。&#xff08;后面讲&#xff09; &#xff08;3&#xff09;方法调用表达式&…

梧州市建设局网站数据来源网站怎么做脚注

问题描述: 我的团队一直在处理一个包含基于标准库的 SD 卡的项目。最近我们决定迁移到 HAL 并开始了。 幸运的是&#xff0c;我们项目的所有部分都尽可能地更改为 HAL&#xff0c;它们运行良好&#xff0c;但我们不知道为什么 SD 卡不能正常运行。 我们没有更改外设的配置时钟&…

读人形机器人30未来20年

读人形机器人30未来20年1. 长期技术可能性 1.1. AI与认知计算的进步1.1.1. 人形机器人进化的核心在于AI和认知计算的进步1.1.2. 未来的机器人将拥有比现今复杂得多的AI系统,能够进行细致入微的理解、基于情境的推理和…

Flutter + Ollama:开启本地AI的全平台新纪元 —— 从零剖析一款现代化AI客户端的技能奥秘

Flutter + Ollama:开启本地AI的全平台新纪元 —— 从零剖析一款现代化AI客户端的技能奥秘pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importa…

wordpress站点改名推广方案框架

from: https://blog.csdn.net/dcrmg/article/details/52851913工业相机基础知识&#xff08;一&#xff09; CCD与CMOS 工业相机按照图像的传感器元件的不同分为CCD&#xff08;Charge Coupled Device,电荷耦合元件&#xff09;和CMOS&#xff08;金属氧化物半导体元件&#xf…

免费网站建设 百度一下手机功能网站案例

如何使用Python获取计算机用户名 一、Python自带的getpass模块可以用于获取用户输入的密码&#xff0c;但是它同样可以用来获取计算机用户名。 import getpassuser getpass.getuser() print("计算机用户名为&#xff1a;", user)二、使用os模块获取用户名 Python的…

产业园区招商团队快躺平了 - 智慧园区

招商人,你是否也曾被这些招商广告刷屏? “0租金”、“0物业费”; “水电暖全免”、“装免期10个月”; “交押金进即可办理入住”; 2025园区招商已经进入白热化阶段,园区之间的竞争早已不在是价值的比拼,已然演变…