文章目录
1.多线程程序日志库要求
-
线程安全,即多个线程可以并发写日志,两个线程的日志消息不会出现交织。
(1)用一个全局的mutex保护IO
(2)每个线程单独写一个日志文件 -
前者造成全部线程抢占一个锁,会造成写日志变成了串行
-
后者有可能让业务线程阻塞在写磁盘操作上,影响业务线程的并发能力
-
解决办法:用一个背景线程(日志线程,后端线程)负责收集日志消息,并写入日志文件,其他业务线程(前端线程)只管往这个“日志线程”发送日志消息(不是实时的),这称为“异步日志”(用非阻塞日志描述更加准确,前端业务线程将日志写到缓冲区中,不涉及到日志文件的写操作,所以可以称之为日志文件的非阻塞操作),并不影响业务线程并发写日志,前端的业务线程不会阻塞的,因为是写到缓冲区中的。
(1)生产者与消费者
前端(业务线程)生产者,多个
后端(日志线程)消费者,1个
缺点:写文件操作比较频繁,效率比较低
生产者
p(semfull)
p(mutex)
往队列中添加一条日志消息
v(mutex)
v(semempty)
消费者
p(semempty)
p(mutex)
从队列中取出所有的日志消息,写入到文件中
v(mutex)
v(semfull)
-
muduo采用多缓冲机制,multiple buffering
前端(业务线程)生产者,多个
后端(日志线程)消费者,1个
优点:使得前端的业务线程与后端的日志线程能够并发,且写日志不太频繁,提高了效率。
(1)前端业务线程将日志写到currentBuffer_,nextBuffer_是后备的一块缓冲区,buffers_是要写入到日志文件的缓冲区列表;
(2)currentBuffer_写满后,添加到buffers_,会通知后端的日志线程写日志文件,currentBuffer_不写满,后端线程是不会将数据写入到日志文件中的,但是此时其他业务线程往nextBuffer_又写入一部分数据了,没写满,此时currentBuffer_需要指向nextBuffer_所指向的缓冲区;
(3)后端日志线程得到通知后,此时前端的currentBuffer_没有满,也会将其添加到buffers_中,前端从buffers_中取出数据;将buwBuffer1_缓冲区给currentBuffer_,将newBuffer2_缓冲区给nextBuffer_,保证前端的业务线程还能写日志消息到缓冲区中。
(4)当buffer_缓冲区的数据写完之后,还需要预留2块缓冲区给newBuffer1_和newBuffer2_
(5)若写日志并不是很频繁,没有办法将一块缓冲区填满的话,经过3s后,也会把没有填满的缓冲区写入到日志文件,即:写入到日志文件不一定是实时的,只要写入到日志文件就行 -
eg:48\jmuduo\muduo\base\AsyncLogging.h
48\jmuduo\muduo\base\AsyncLogging.cc -
eg测试:48\jmuduo\muduo\base\tests\AsyncLogging_test.cc
48\jmuduo\muduo\base\tests\CMakeLists.txt
48\jmuduo\muduo\base\CMakeLists.txt -
测试:下面的每行是每次写入1000条消息的时间
生成的日志文件
注释掉下面的代码,产生消息堆积,回丢失一部分消息,日志也没那么大了
struct timespec ts = { 0, 500*1000*1000 };//睡眠0.5秒
nanosleep(&ts, NULL);