• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2023-09-16 16:26 Aet 隐藏边栏 |   抢沙发  9 
文章评分 1 次,平均分 5.0

线程池

创建线程池方法一

  1. 调用async_factory_impl::create创建线程池
    1. 但是这个函数实际作用是用于创建一个记录器
    2. 由于需要一个线程池对象参数,所以在创建记录器之前做了个检查,如果还不存在线程池,就先创建线程池,再构造记录器

  1. 这里使用递归锁的原因可能是为了使代码的防御性强一些
  2. thread_pool类的解析看后文

创建线程池方法二

  1. 调用init_thread_pool创建线程池

  1. s_thread_pool是一个静态的std::shared_ptr对象

  1. static对象只会被初始化一次,并且:
    1. C++11及之后的版本中,函数内的局部静态对象的初始化是线程安全的
  2. 可以看到用上面这个方法创建线程池后,会被转移到了class SPDLOG_API registry这个类的实例里面
    1. 而这个类class SPDLOG_API registry是一个单例类
    2. class SPDLOG_API registry解析见下文

获取线程池方法三

  1. 如方法二中所述,用方法二创建的线程池会被保存到class SPDLOG_API registry这个类的静态实例里面
    1. 所以可以通过这个类的接口来获取,这个类提供了这样的方法

thread_pool类

  1. q_是一个队列,是在线程池构造的初始化列表里面,通过调用这个队列的构造函数来初始化
    1. 根据上面的代码,可以看到,它一般被初始化为下面的大小

  1. 在构造函数里,检查了要创建的线程的数量,为0或过大,就抛一个异常出去
  2. 然后创建所要求的数量的线程,并保存在了vector里面
    1. 可以看到,是通过传入了一个lambda来隐式生成了一个线程对象

  1. process_next_msg_这个函数相当于是线程池里线程的消息泵
    1. 只有当这个函数返回false的时候,该线程结束
    2. 所以销毁线程的时候,也是利用了这一点,通过传入了类型为terminate的消息包,并调用notify_one来通知一个线程来处理这个消息包
    3. process_next_msg_里面,发现消息包的类型为terminate,于是返回了false,从而结束了这个线程

  1. 消息泵函数里面,则通过队列的dequeue_for这个函数来获取一个消息包
    1. 没有拿到消息包,返回true,从而再次进入消息泵
    2. 拿到了消息包,根据消息包的类型来处理
    3. 消息包里,不仅存了消息包的类型,还存了一个记录器的智能指针对象
    4. 对于log类型的消息,用存着的智能指针对象去记录
    5. 对于flush类型的消息,用存着的智能指针对象去调用fflush
    6. 对于terminate类型的消息,返回false,让线程结束

数据队列

  1. pop_cv_在检查队列,只要队列没满,就能往里面添加数据,成功添加一个数据,便push_cv_通知一下

  1. 立即加入队列的版本

  1. 取数据,这个函数正是上面线程的消息泵取数据的地方
    1. push_cv_在等待数据,一段时间内,队列还是为空就返回
    2. 不为空就取出来一个消息,而这个函数的第一个参数是出参,调这个函数的地方,会对这个消息包进行处理,上文有讲
    3. 同时,每取出一个消息包,就通知一下pop_cv_这个条件变量,而它不再阻塞,开始继续检查队列是不是已满

  1. 查询队列数据
    1. 这里上锁是为了确保在多线程环境中数据的一致性和线程安全

其他接口

  1. post_log
    1. 往线程池里投递消息log消息包
  2. post_flush
    1. 往线程池里投递消息flush消息包

registry类

记录器

  1. 会在创建registry类的时候创建一个默认记录器,默认记录器没有名字

  1. 可以往registry里面注册记录器
    1. 可以看到,注册记录器就是,registry用了一个map把每一个记录器的智能指针对象保存了起来

  1. 初始化记录器,对一个新的记录器进行初始化,并注册

  1. registry里面用记录器名字获取记录器
    1. 它这些查找都是加锁的

默认记录器

  1. 它不仅被存到了registry里面,没有使用std::move
  2. 而且还用单独的default_logger_来存储默认记录器
  3. 当然,也有更新默认记录器的方法

线程池

  1. 获取

记录器->接收器->格式器

  1. 设置
    1. 可以看到,调用这个接口设置一个格式器后,不仅被保存了,而且还被用于更新每一个记录器的格式器

  1. 格式器是接收器的组件
    1. 具象化的接收器,每一个都实现了基类接收器设置格式器的抽象方法

记录器->回溯器

  1. 每个记录器还有一个回溯器
    1. 可以看到,回溯器有个环形缓冲区

  1. 打印消息的时候,会根据记录器的回溯器的标志,来判断是否把消息添加到回溯器中

  1. 打印回溯信息
    1. 记录器给出了接口,通过回溯器的foreach_pop接口获取回溯器里面的每一个消息包,并打印信息

类图

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享