一、基础知识
1. 概念

流程图[2]
- 日志:由服务器自动创建,用于记录运行状态和错误信息;
- 同步日志:日志写入函数与工作线程串行执行,由于涉及到 I/O 操作,当单条日志较大时,同步模式会阻塞整个处理流程,服务器所能处理的并发能力将有所下降,易称为性能的瓶颈;
- 异步日志:将所写的日志内容先存入缓存队列,写线程从阻塞队列中去除内容,写入日志;
- 生产者/消费者模型:生产者与消费者共享一个缓冲区,生产者往缓冲区添加消息,消费者从缓冲区中处理消息;
- 阻塞队列:将生产者/消费者模型进行封装,使用循环数组实现队列,作为两者共享的缓存区;
- 单例模式:保证一个类只创建一个实例,同时提供全局访问的方法,单例模式有懒汉模式与饿汉模式,前者在第一次使用时进行初始化,后者在程序运行时初始化;
- 日志分级:一般提供五种级别:
- Debug :调试代码时的输出,在系统实际运行时,一般不使用;
- Warn :调试代码时使用的终端警告;
- Info :报告系统当前的状态,当前执行的流程或接收的信息等;
- Error 和 Fatal :输出系统的错误信息;
- 日志分文件:根据日期、行数判断是否需要份文件;
2. API
pthread_cond_init函数:用于初始化条件变量;
pthread_cond_destory函数:销毁条件变量;
pthread_cond_broadcast函数:以广播的方式唤醒所有等待目标条件变量的线程;
pthread_cond_wait函数:用于等待目标条件变量,调用时传入 mutex 参数(加锁的互斥锁),执行时先把调用线程放入条件变量的请求队列,然后将互斥锁解锁,成功时返回 0 ,表示重新抢到了互斥锁,将其再次锁上,因此函数内部会有一次解锁和加锁操作,使用方法如下:
- 由 API 介绍可知,在使用
pthread_cond_wait函数前需要加锁,这是为了避免资源竞争,使得每个线程互斥访问公有资源。
- 在函数内部将互斥锁解锁,是因为函数会阻塞自己,此时它仍持有互斥锁,若不解锁则其他线程无法访问公有资源。
- 在对互斥锁解锁前需要将调用线程放入请求队列,这是因为如果在把调用线程放入等待队列之前就释放互斥锁,会导致其他线程获得互斥锁从而访问公有资源,此时调用线程锁等待的条件改变了,但是它没有被放在等待队列上,导致调用线程忽略了等待条件被满足的信号,发生错误。
- 当调用线程放在条件变量之后,将互斥锁解锁,此时等待被唤醒,若成功竞争到互斥锁,再次加锁来访问公共资源。
int fputs(const char *str, FILE *stream);:
str:一个包含了要写入的以空字符终止的字符序列;
stream:指向 FILE 对象的指针,标识了被写入字符串的流;
__VA_ARGS__:可变参数的宏,可以在宏定义中使用省略号,若加##则更健壮,可以在可变参数为 0 时将逗号去掉:
int fflush(FILE *stream);:强迫缓存区内的数据协会参数stream指定的文件中,如果参数为 NULL ,则会将所有打开的文件数据更新。该函数主要用于防止新到的数据冲掉未输出完数据的缓冲区(即覆盖了原有数据),通常将其放在printf函数之后。
二、代码解析
1. 单例模式
经典的懒汉模式:
优雅的懒汉模式:
饿汉模式:
2. 阻塞队列
3. 日志类定义
4. 生成日志文件并判断写入方式
5. 日志分级与分文件
参考文献
原创存证 · 本地哈希 · 3fbaa1812b4d...
算法:SHA-256
时间:2026-10-16
哈希:
3fbaa1812b4d35317803f6ab7d009335287cf2f6bf5885f36938443faf8d054b该信息用于辅助证明当前内容版本,不等同于法律公证或版权登记。
- 作者:BeZer0
- 链接:https://blog.bezer0.cn/%E8%80%83%E7%A0%94/3fb0bc13-b7ac-8033-8cb5-e64446a9fa9d
- 声明:本文为原创文章,采用 CC BY-NC-SA 4.0 协议,转载请注明作者和原文链接。

