c++如何实现一个带双缓冲区的异步高性能文件日志落盘框架类设计【进阶】

陌磊君_7334

陌磊君_7334

2026-04-06

1021人浏览

原创

双缓冲区需用固定大小std::array或自定义栈式buffer,buffer_a与buffer_b通过原子指针无锁切换;应用线程仅append(),后台线程flush()时原子交换并只读访问;推荐专用flush线程+write()配合fdatasync(),状态用std::atomic_flag管理,日志采用定长前缀+可变体格式。

c++如何实现一个带双缓冲区的异步高性能文件日志落盘框架类设计【进阶】

双缓冲区结构怎么组织才不丢日志又不卡主线程

核心是两块独立内存区域交替写入,一块被应用线程填充时,另一块由后台线程刷盘。不能用 std::vector<char></char> 动态扩容——写一半触发 realloc 会破坏原子性;也不能直接裸指针配 new[]——异常安全难保障。推荐用固定大小的 std::array<char n></char> 或自定义栈式 buffer 类,大小取 4KB~64KB(兼顾页对齐与 L1/L2 缓存友好)。

关键约束:buffer_a 和 buffer_b 必须全程无锁切换,靠原子指针或原子索引控制所有权。切忌在切换瞬间 memcpy 内容——这等于把性能瓶颈从 I/O 挪回 CPU。

  • 应用线程只调用 append(),内部仅做指针偏移 + 字符拷贝,不涉及系统调用
  • 后台线程调用 flush() 时,必须先原子交换出待刷盘 buffer,再对其只读访问
  • buffer 切换前需检查是否已满,满则阻塞式等待(或丢弃/告警),但绝不能自旋空转耗尽 CPU

异步刷盘用 write() 还是 io_uring 或 libaio

Linux 下普通 write() 调用本身是同步的,即使文件以 O_NONBLOCK 打开,对常规文件也无效;O_DIRECT 又要求地址对齐、长度对齐、无 page cache,反而增加开发负担且小日志写入性能更差。真正可行的是:

  • POSIX AIO(aio_write()):内核支持度好,但回调机制笨重,错误码分散,且 glibc 实现实际是线程池模拟,并非真异步
  • io_uring(5.1+):零拷贝提交、批量提交、支持 IORING_OP_WRITE,是目前最优选;但需手动管理 io_uring_sqe 生命周期,且 fallback 逻辑要写全
  • 线程池 + 阻塞 write():最简单可靠,配合 posix_fadvise(fd, offset, len, POSIX_FADV_DONTNEED) 减少 page cache 压力,实测吞吐不输 AIO

除非业务强依赖 kernel 6.0+ 且已有 io_uring 基础设施,否则优先选「专用 flush 线程 + write()」:它规避了上下文切换抖动、避免了 ring buffer 管理复杂度,且便于调试(strace -e write 直接可见)。

std::mutex 和 std::atomic_flag 在 buffer 切换中怎么选

buffer 切换本质是“发布-消费”模型:应用线程生产完一整块数据后,需原子通知 flush 线程可以消费;flush 线程消费完后,需原子通知应用线程可复用该 buffer。这里不用互斥锁,而用 std::atomic_flag 或 std::atomic<int></int> 控制状态机。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

典型状态值:0=空闲、1=正在写入、2=待刷盘、3=正在刷盘。切换时用 compare_exchange_strong() 保证状态跃迁合法。例如从 1→2 必须成功才允许 flush 线程拿走 buffer;若失败说明应用线程还没写完,flush 线程应 yield 或短暂 sleep。

  • 绝不要用 std::mutex 保护 buffer 指针——锁粒度太大,会串行化所有日志追加
  • 也不要用 std::condition_variable 唤醒 flush 线程——唤醒时机难精准,易导致延迟毛刺
  • 更别用自旋锁(while(!flag.test_and_set()))——高负载下吃光 CPU,且无法让出调度权

日志格式与落盘一致性怎么兼顾性能和可查性

纯二进制格式最快,但调试抓包反人类;纯文本最易读,但 strftime() + snprintf() 占用可观 CPU。折中方案是「定长前缀 + 可变体」:前 32 字节固定为时间戳(微秒级,用 clock_gettime(CLOCK_MONOTONIC, &ts))、线程 ID、日志等级、长度字段;后续紧跟原始字符串(不带换行,由落盘线程统一加 \n)。

为防断电丢日志,每次 write() 后必须跟 fsync() 或 fdatasync()。但频繁 fsync 会拖垮性能,所以只在以下任一条件满足时触发:

  • buffer 满(强制落盘)
  • 距离上次 fsync 超过 1s(保延迟上限)
  • 日志等级为 FATAL 或 ERROR(关键错误必须立即持久化)

注意:fdatasync() 比 fsync() 轻量(不刷 metadata),但某些旧文件系统(如 ext3)不支持,需运行时探测。

缓冲区大小、刷盘时机、状态同步方式这三者必须联合调优,改一个就可能让延迟分布从 99%

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.25

354

5

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.18

969

5

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.25

2820

5

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.03

1698

5

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

2584

5

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

2023.10.24

6004

49

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

2023.11.24

5489

6

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

2024.03.22

7174

16

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

2024.04.29

10294

11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习