项目八股[日志系统]

日志系统涉及到的C++特性语法 用了一个锁+两个条件变量,跟线程池不一样 只用了一个锁一个条件变量

C++11提供的 condition_variable 类是一个同步原语,它能够阻塞一个或者多个线程,直到另一线程修改共享变量并通知 condition_variable。

对比POSIX的pthread_cond,pthread移植性好,condition_variable则是配合RAII机制更为灵活,像unique_lock可以随时加锁不用手动,离开作用域后自动解锁

1.单例模式

2.lock_guard<mutex> locker(mtx);

lock_guard和unique_lock怎么区分,我发现代码里用到了锁+条件变量的地方就会用unique_lock,其他地方都是单lock_guard

 简单地讲,unique_lock 是 lock_guard 的升级加强版,它具有 lock_guard 的所有功能,同时又具有其他很多方法,使用起来更强灵活方便,能够应对更复杂的锁定需要。

  • 作用域规则同 lock_grard,析构时自动释放锁
  • 不可复制,可移动
  • 条件变量需要该类型的锁作为参数(此时必须使用 unique_lock)

 

3.fputs(str.c_str(), fp);

4.fflush(fp);

 

 

阻塞队列

队列是容器适配器,默认实现底层用的deque,deque本身是一个序列式容器,它的迭代器不支持随机访问,所以查找效率不高O(n)。插入删除效率比较高 因为不需要移动后续元素O(1)

vector也是个序列式容器,因为支持随机下标访问,所以查找效率很高O(1),但是它除了在头尾插入比较高效,其他位置插入都需要移动后续的元素,所以插入复杂度O(n)。

所以总体来说是看构造这个容器适配器时的需求,如果说不要求频繁的随机访问,那么用deque,如果说不要求在中间进行频繁插入删除,那么用vector,其他情况下两者都行。

 

 

日志系统是懒汉模式,也就是C++11之后的局部静态实现的单例模式,内部会维护一个日志消息队列,作为采用异步模式下的写入中介。日志系统头文件里定义了四个宏作为写入的接口方便在主线程与工作线程需要的地方进行传参使用。

日志系统是一个典型的生产者消费者模型,其他线程是生产者,日志线程是唯一的消费者,他们之间进行数据交互是依赖日志消息队列,所以日志消息队列的设计需要解决线程安全(互斥与同步的问题)和预防日志队列过度增长导致长时间高频触发增长使得资源耗尽的问题。

template<class T>
void BlockQueue<T>::push(const T &item) {
    std::unique_lock<std::mutex> locker(mtx);
    while(que.size() >= capacity_) {
        condProducer.wait(locker);
    }
    que.push(item);
    condConsumer.notify_one();
}

为什么有锁还要用条件变量?

为什么要单独一个日志线程 因为日志线程要进行大量的对同一个日志文件的写入,很多线程互斥着等待上锁读写效率很低。 同步的话生产者等待写入日志的函数返回效率也低。所以异步管理比较好。

因为条件变量能实现逻辑功能的同步问题,如果没有条件变量,生产者的情况会变成,多个生产者在反反复复的上锁 查看可否写入 再解锁 但实际上只有消费者抢到锁了 消费了一个日志信息,生产者才有写入的条件,而此时消费者抢到锁的概率很很低了。、

中级挂起再唤醒抢锁效率很低,不如用条件变量去维护逻辑顺序的同步,也就是用到两个条件变量进行互相唤醒,生产者写入了唤醒消费者,消费者消费了唤醒生产者。

为什么要用while循环而不是if 因为会存在虚假唤醒的可能。

比如有ab两个生产者进程 一个c消费者进程,生产效率高导致队列逐渐满了,然后a线程率先因为队列满了写不下了而进入条件变量wait,它进入条件变量的条件是队列满了,可是醒来并试图往下执行的时候,队列就一定是空的吗?不一定,因为可能c在试图唤醒生产者所有线程的时候,b却没在wait的等待队列里,甚至还没进while循环,但它此时抢到锁了(然后没进入while就向下执行)并且又把队列满上了。而a呢,a因为自己是不满足一个条件之后才wait并且释放锁的,它处在条件变量的等待队列里,第一次唤醒时没能抢到锁,后续不用再等待条件变量了,只要抢到锁就应该能向下执行,但它能上锁的时候,条件(队列没满能向下执行)却未必满足了,但它没用while循环去再次判断条件,所以它在队列满着的情况下向下执行,此时就会出问题。

而用while循环,醒来之后要向下执行首先需要再次判断一下while,就避免了在队列已经达到预期上限情况时还向下执行的问题。

 

posted @ 2023-09-08 19:23  timeMachine331  阅读(47)  评论(0)    收藏  举报