log4cpp源码阅读:NDC类学习

2023-11-09 16:59
文章标签 源码 学习 阅读 log4cpp ndc

本文主要是介绍log4cpp源码阅读:NDC类学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介

  • 位置:
    • include/log4cpp/NDC.hh
    • src/NDC.cpp
  • NDC全称是nested device context,其内部提供的方法都是线程安全的。并且这种实现安全的手段不是通过threading::mutex,而是通过threading::ThreadLocalDataHolder来实现的
  class LOG4CPP_EXPORT NDC {...  }

内部成员

它的内部成员实际上只有一个 std::vector<DiagnosticContext>一个成员,所以我们先来看一下DiagnosticContext的实现。

DiagnosticContext内部类

实质上是NDC的某些操作的基本单元。内部结构如下

    struct DiagnosticContext {DiagnosticContext(const std::string& message);DiagnosticContext(const std::string& message, const DiagnosticContext& parent);std::string message;  // 当前的消息std::string fullMessage;   // 所有的消息= 上次所有消息 + 这次设置的消息};
NDC::DiagnosticContext::DiagnosticContext(const std::string& message) :message(message),fullMessage(message) {}NDC::DiagnosticContext::DiagnosticContext(const std::string& message, const DiagnosticContext& parent) :message(message),fullMessage(parent.fullMessage + " " + message) {}

从源码中可以看到,假如有如下一组对象:
在这里插入图片描述
即:fullMessage的内容总是包含前一次的内容,而message包含的是当前的内容。

ContextStack

typedef std::vector<DiagnosticContext> ContextStack;

ContextStack是NDC内部定义的一个std::vector<DiagnosticContext>的别名,它是NDC的一个主要成员的基本类型,并且使用这种类型完成了一个叫做Context栈的实现,即可以入栈push,还可以获取栈顶元素,还可以出栈。

NDC的实现

它内部包含了一个ContextStack类型的对象,还有两个只是辅助某些功能实现的标志类似的东西。这个类可以执行线程安全的将某个字符串进行入栈操作、还可以获取栈顶元素,出栈。

class NDC {
public:typedef std::vector<DiagnosticContext> ContextStack;    //定义的一种类型别名static NDC &getNDC(); //从线程局部获取当前线程的NDC对象...private:ContextStack _stack;    //内部的一个具体成员
};

这儿有一个特别的方法,getNDC(),这个方法是这个类的工厂方法,他还有一点特别的是,是什么呢?看一下他的源码实现:

namespace {threading::ThreadLocalDataHolder<NDC> _nDC; //_nDC命名为【ndcThreadLocal】更加贴切   }NDC& NDC::getNDC() {NDC* nDC = _nDC.get();if (!nDC) {nDC = new NDC();_nDC.reset(nDC);}return *nDC;}

这儿的代码逻辑有点像单例吗,其实不然,首先ndc指针是从线程局部中取的获取一个ndc值,因为第一次去的时候肯定为空,这个实现 就需要new一个值放入到这个线程局部中。并且将此只返回,当下次再从线程局部中取值的时候,取得就是上次设置的值。

这样就能做到每一个线程一个NDC了:

#include <iostream>
#include <thread>
#include <log4cpp/NDC.hh>
using namespace std;
using namespace log4cpp;void thread_proc() {cout <<  "sub thread:" << &NDC::getNDC() << endl;
}int main() {cout <<  "main thread:" << &NDC::getNDC() << endl;thread(thread_proc).join();return 0;
}

结果:

main thread: 0066CB80
sub thread:00687AB8

从而实现了线程安全。这个方法是NDC中最精彩的一个方法。其他的静态方法,其实内部都是调用获取的NDC对象的真正实现方法

入栈操作(Push)

class NDC {
public:static bool _isUsedNDC;static void push(const std::string& message);
};
bool NDC::_isUsedNDC = false;void NDC::push(const std::string& message) {if (!isUsedNDC)isUsedNDC = true;getNDC()._push(message);}

push方法(入栈方法,静态方法),这儿会检测一个标志是否为false,如果为false则设置为true,然后调用的是对象真正的实现方法。这儿为什么要定义一个 _isUsedNDC成员,我们可以再后面的代码中看到。

获取栈顶元素(Get)

class NDC {
public:static std::string pop();
};const std::string& NDC::get() {if (isUsedNDC)return getNDC()._get();elsereturn emptyString;}

getNDC()方法内部调用的是 NDC::getNDC(),只要获取的话,那么它的内部就会创建一个新的NDC对象(第一次调用),现在这个NDC对象中是没有数据的,所以这次调用不就白调用了吗?而且有可能是误调用,只有有数值放进来的时候, 才会将这个标志设置为空,那么下次再调用get()方法的时候,就可以很自然的获取数据了。

出栈操作(Pop)

class NDC{static std::string pop();
}std::string NDC::pop() {return getNDC()._pop();}

作用:会将栈顶元素内容弹出。真正的实现部分是_pop()方法

删除所有元素(Clear)


class NDC{static void clear();
}void NDC::clear() {getNDC()._clear();}

真正的实现部分是_clear()方法

真正实现

上面的这几个静态方法都是各个方法所对应的同语义的非静态方法,源码如下:

    // 对应静态get方法的真正非静态实现// 出栈,取得是最后一个元素(每一个元素是DiagnosticContext类型)的fullMessageconst std::string& NDC::_get() const {static std::string empty = "";return (_stack.empty() ? empty : _stack.back().fullMessage);}// 真正入栈实现部分,实现稍微需要注意,因为DiagnosticContext有两个参数,所以当第一次压栈的时候,需要做特别处理。 然后以后每次压栈,都是将栈顶元素作为新的DiagnosticContext的父对象存在的void NDC::_push(const std::string& message) {if (_stack.empty()) {_stack.push_back(DiagnosticContext(message));} else {            _stack.push_back(DiagnosticContext(message, _stack.back()));}}// 清除_stack中的所有内容非常简单void NDC::_clear() {_stack.clear();}std::string NDC::_pop() {// 出栈操作,会将栈顶元素返回(就是最后一次入栈的元素原封不动的返回)if (!_stack.empty()) { // 必须首先检测一下std::string result = _stack.back().message;_stack.pop_back();return result;} else {return emptyString;}}

其实上面的入栈操作,出栈操作,获取栈顶元素,清除所有,都是对内部数据_contextStack(std::vector)的一种操作

其他方法

class NDC {// new的当前的_contextStack的拷贝将它返回static ContextStack *clone() {return getNDC()._cloneStack();}// 获取深度,实际上返回的就是_contextStack的size()static size_t getDepth() {return getNDC()._getDepth();}// 他的真正实现为空,因为ndc内部使用的容器是vector,他可以存放任意多的元素static void setMaxDepth(int maxDepth) {getNDC()._setMaxDepth(maxDepth);}// 实现就是将stack栈中的内容,拷贝到当前线程的_stackContext中// 用处:实现将父类线程中的_stackContext内容拷贝到子线程的_stackContext中static void inherti(ContextStack *stack) {return getNDC()._inhert(stack);}
};
    NDC::ContextStack* NDC::_cloneStack() {return new ContextStack(_stack);  // std::vector()的拷贝构造}size_t NDC::_getDepth() const {return _stack.size();   //vector.size()}void NDC::_inherit(NDC::ContextStack* stack) {_stack = *stack;  //当前线程的_stack 拷贝stack中的内容}void NDC::_setMaxDepth(int maxDepth) {// XXX no maximum   }

NDC经常用的方法:

  • 静态方法push,用来将指定字符串压栈
  • 静态方法get,获取栈顶元素,返回的字符串包含所有已经压栈的字符串
  • 静态方法pop,用于出栈操作,返回的上次压入过来的字符串
  • 静态方法clear,用来删除栈中所有的元素

总结

  • NDC实现 = std::vector + 线程局部变量
  • NDC 是一个使用线程局部存储的一个不用加锁的多线程数据能够实现安全访问的技术。

这篇关于log4cpp源码阅读:NDC类学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/zhizhengguan/article/details/123143900
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/377412

相关文章

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory