多线程异步【日志系统】,高效、强悍的实现方式:双缓冲!
扫描二维码
随时随地手机看文章
目录
如果您的主力开发语言是 C ,强烈推荐您去研究下这本书。言归正传!很多 C 语言的细节问题,作者都给出了自己专业、严谨的思考和解决方案。
单片机中常用的环形缓冲区
一说到缓冲区,相信各位小伙伴一定看过很多关于缓冲缓冲区的文章和代码,在单片机中的使用率很高。
所谓的环形缓冲区,就是一块平整的内存区域,让它的尾部连接到首部即可。
另一个类似的结构:环形队列,本质上都是一样的。维护环形缓冲区的数据结构中,有head和tail指针。
这样的线程称作 前台/前端 线程。日志数据存储在内存中之后,最终是要输出的,比如:写入到文件系统、通过网络上传到服务端、输出到其他的监控系统等等。
这样的线程称作 后台/后端 线程。但是,文件系统的写入速度是很慢的(毕竟要操作硬盘啊),如果这个时候又有前台线程需要写日志信息了,该如何处理?
多线程异步日志:双缓冲机制
在这本书中,作者对这样的日志系统规定了几个关键的要求,都是与实际的业务需求相关的:
为了达到这个目的,作者提出了“双缓冲”思路(Double Buffering)。
- 线程安全:多个线程可以并发写日志,不造成竞争,两个线程的日志信息不会交叉出现;
- 吞吐量大;
- 日志消息有多种级别,格式可配置等等;
当 buffer A 写满之后,交换两个缓冲区:
双缓冲机制为什么高效
使用两个buffer缓冲区的好处是:
例如:可以根据实际使用场景,定义一个刷新频率,例如:3秒。换言之,前端线程不是将一条条日志信息分别传送给后端线程,而是将多条信息拼成一个大的 buffer 传送给后端,相当于是批量处理,减少了线程唤醒的频率,降低开销。只要刷新时间到了,即使缓冲区中的日志信息很少,也要把它们存储到文件系统中。
尽可能的降低 Lock 的时间
在刚才的描述中,有这么一句话:在[大部分的时间中],前台线程和后台线程不会操作同一个缓冲区。
在第2个步骤中:交换缓冲区,就是把两个指针变量的值交换一下而已,利用C语言中的swap操作,效率很高。
- 后台线程被唤醒,此时 buffer B 缓冲区是空的,因为在上一次进入睡眠之前,buffer B 中数据已经被写入到文件系统中了;
- 把 buffer A 与 buffer B 进行交换;
- 把 buffer B 中的数据写入到文件系统;
- 开始休眠;
参考代码
在示例代码中,作者对双缓冲机制进行了扩展,采用4个缓冲区,这样可以进一步减少或避免前端线程的等待时间。
这里的nextBuffer_相当有是currentBuffer_的“备胎”。
前端线程在生成一条日志消息的时候,会调用append()函数。
move 是 C 中的操作,意思是移动,而不是拷贝/复制。当然了,如果前端的写入速度太快,一下子就把两块缓冲区都用完了,那么只好分配一块新的 buffer 作为当前缓冲区,这是极少发生的情况。
这段代码中最重要的就是 swap 函数,它把前后台使用的缓冲区进行了交换。
可以继续优化的地方
在本章的最后部分,作者提出了一个更加严苛的情况:
------ End ------





