1.EasyLogger源码学习笔记(1)
2.学习编程|Spring源码深度解析 读书笔记 第4章:bean的程序程序加载
3.Vuex 4源码学习笔记 - mapState、mapGetters、源码员必mapActions、笔记mapMutations辅助函数原理(六)
4.EasyLogger源码学习笔记(4)
5.EasyLogger源码学习笔记(5)
6.EasyLogger源码学习笔记(3)
EasyLogger源码学习笔记(1)
在编程中,读源预处理器通过宏定义执行特定的程序程序逻辑。使用`#ifdef`和`#else`可以实现条件编译。源码员必源码咖啡当`#ifdef _XXXX`中的笔记标识符_XXXX被`#define`命令定义时,编译器将执行`#ifdef`后的读源程序段1,否则执行`#else`后的程序程序程序段2。`#ifndef _XXXX`则表示如果标识符未被定义,源码员必则执行程序段1,笔记反之执行程序段2。读源
ANSI C宏提供了多种实用信息,程序程序如`__DATE__`返回当前日期,源码员必`__TIME__`返回当前时间,笔记`__FILE__`包含当前文件名,`__LINE__`包含当前行号。`__STDC__`常量用于判断程序是否遵循ANSI C标准。`__FUNCTION__`宏在预编译时返回所在函数的名称。
宏参数的处理可以通过`#`将参数变为字符串,使用`##`将两个宏参数连接起来。`__VA_ARGS__`是一个可变参数宏,需配合`define`使用,将宏左侧的`..`内容原样复制到右侧。
`#if defined`和`#if !defined`在功能上相似,都用于判断宏是否定义。`#error`指令在编译时生成错误消息并停止编译,用于警告开发者。
`extern`关键字用于引用其他文件中的函数或全局变量。例如`extern ElogErrCode elog_port_init(void);`声明了一个名为`elog_port_init`的外部函数,调用时需要指明返回值类型和参数。
在多线程编程中,使用`sched_param`结构来管理线程调度参数。`sem_t`表示信号量,用于实现互斥和同步。`pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);`设置进程调度策略为实时轮转调度。
`SCHED_OTHER`默认分时调度策略,`SCHED_FIFO`采用先进先出策略,而`SCHED_RR`是`SCHED_FIFO`的增强版,提供实时轮转功能。使用`sched_get_priority_max(int policy);`和`sched_get_priority_min(int policy);`函数可以获取线程可设置的714网贷源码最高和最低优先级,其中策略参数即上述三种调度策略的宏定义。
`pthread_attr_setschedparam(&thread_attr, &thread_sched_param);`用于设置线程的优先级。通过这些函数,开发者可以精细地控制线程调度,提高程序性能。
学习编程|Spring源码深度解析 读书笔记 第4章:bean的加载
在Spring框架中,bean的加载过程是一个精细且有序的过程。首先,当需要加载bean时,Spring会尝试通过转换beanName来识别目标对象,可能涉及到别名或FactoryBean的识别。
加载过程分为几步:从缓存查找单例,Spring容器内单例只创建一次,若缓存中无数据,会尝试从singletonFactories寻找。接着是bean的实例化,从缓存获取原始状态后,可能需要进一步处理以符合预期状态。
原型模式的依赖检查是单例模式特有的,用来避免循环依赖问题。然后,如果缓存中无数据,会检查parentBeanFactory,递归加载配置。BeanDefinition会被转换为RootBeanDefinition,合并父类属性,确保依赖的正确初始化。
Spring根据不同的scope策略创建bean,如singleton、prototype等。类型转换是后续步骤,可能将返回的bean转换为所需的类型。FactoryBean的使用提供了灵活的实例化逻辑,用户自定义创建bean的过程。
当bean为FactoryBean时,getBean()方法代理了FactoryBean的getObject(),允许通过不同的方式配置bean。缓存中获取单例时,会执行循环依赖检测和性能优化。最后,直播盒子源码安装通过ObjectFactory实例singletonFactory定义bean的完整加载逻辑,包括回调方法用于处理单例创建前后的状态。
Vuex 4源码学习笔记 - mapState、mapGetters、mapActions、mapMutations辅助函数原理(六)
在前一章中,我们通过了解Vuex的dispatch功能,逐步探索了Vuex数据流的核心工作机制。通过这一过程,我们对Vuex的整体运行流程有了清晰的把握,为深入理解其细节奠定了基础。本章节,我们将聚焦于Vuex的辅助函数,包括mapState、mapGetters、mapActions、mapMutations以及createNamespacedHelpers,这些函数旨在简化我们的开发流程,使其更符合实际应用需求。
请注意,这些辅助函数在Vue 3的Composition API中不适用,因为它们依赖于组件实例(this),而在Setup阶段,this尚未被创建。因此,它们仅适用于基于选项的Vue 2或Vue 3经典API。
以mapState为例,它允许我们以计算属性的形式访问Vuex中的状态。当组件需要获取多个状态时,通过mapState生成的计算属性可以显著减少代码冗余。若映射的计算属性名称与state子节点名称相同,只需传入字符串数组。此外,通过对象展开运算符,我们能轻松地在已有计算属性中添加新的映射。
深入代码层面,mapState的核心功能在src/helpers.js文件中得以实现。通过normalizeNamespace函数统一处理命名空间和map数据,然后利用normalizeMap函数将数组或对象格式数据标准化,最终返回一个封装后的起爆量指标源码函数对象。通过这种方式,mapState有效简化了状态访问的实现。
mapGetters、mapMutations、mapActions遵循相似的模式,通过normalizeNamespace统一输入,然后使用normalizeMap统一数据处理,最后返回对象格式的函数集合,支持对象展开运算符的使用。这些函数简化了获取、执行actions和mutations的过程。
createNamespacedHelpers则是为管理命名空间模块提供便利。通过传入命名空间值,它生成一组组件绑定辅助函数,简化了针对特定命名空间的模块操作。此函数通过bind方法巧妙地将namespace参数绑定到返回的函数集合中,实现了高效、灵活的命名空间管理。
本章节对mapState的实现原理进行了深入分析,并展示了其余辅助函数的相似之处。通过理解这些函数的工作机制,我们能更高效地应用Vuex,优化组件间的交互与状态管理。利用这些工具,开发者能够更专注于业务逻辑的实现,而不是繁琐的状态获取和管理。
在探索更多前端知识的旅程中,让我们一起关注公众号小帅的编程笔记,每天更新精彩内容,与编程社区一同成长。
EasyLogger源码学习笔记(4)
setbuf函数用于开启或关闭缓冲机制,关闭时使用setbuf(stdout, NULL);。
在编程中,unlikely(x) 和 likely(x) 函数通过宏定义 __builtin_expect(!!(x), 1) 和 __builtin_expect(!!(x), 0) 实现,用以帮助优化编译器,实现等价于if(a)但更高效的条件判断。
semget()函数用于创建或获取信号量,其原型为 int semget(key_t key, int num_sems, int sem_flags)。它接受一个键值、指定信号量数量及标志位,查看PHP源码报错成功时返回信号标识符,失败时返回-1。
semctl()函数用于设置或获取信号量的值,而semop()函数则用于执行信号量的P操作或V操作。
信号量在共享内存管理中扮演关键角色,内核维护一个名为shmid_ds的数据结构,用于管理共享内存段。
利用fseek()函数,可以设置文件流的位置,通过参数offset和whence来确定查找位置的偏移量。
a+方式打开文本文件,允许读写,若文件不存在则创建,读取从头开始,写入只能追加。
sem_post函数(int sem_post(sem_t *sem);)将信号量值增加1,当线程阻塞在该信号量上时,调用此函数会使一个线程解除阻塞,选择机制由线程调度策略决定。
sem_wait函数(int sem_wait(sem_t * sem);)则将信号量值减去1,但需等待信号量值非零时才开始减法操作。
一种应用方法是利用信号量实现类似于信号传递的功能,某线程在特定条件下执行任务,其他线程通过调用sem_post()使信号量加一,该线程在调用sem_wait()后解除阻塞,继续执行。
EasyLogger源码学习笔记(5)
在EasyLogger源码的学习中,我们了解到日志对象使用了互斥锁以确保同一时刻只有一个线程能进行操作,保证了日志管理的安全性与高效性。
对于异步输出,EasyLogger通过信号量实现了优化。当需要等待执行时,某个线程会被阻塞,以减少CPU的占用。这一特性允许用户单独设置异步输出的日志等级,提高系统的灵活性与可控性。
在文件输出时,使用了信号量集合,其中仅包含一个信号量。这一设计确保了同时只有一个线程能向文件中写入日志,避免了多线程并发写入导致的文件混乱。
日志输出的多样选择体现了EasyLogger的灵活性,无论是输出到文件还是串口,都可以根据需要配置是否采用异步输出,以适应不同的应用场景与性能需求。
此外,sem_post函数用于解锁由semby指定的信号量,执行对特定信号量的解锁操作。而semop函数则用于执行一组预先定义的信号量操作,适用于对多个信号量进行原子性操作。
在信号量集合仅包含一个信号量的情况下,使用sem_post函数进行操作可能直接替代使用semop函数。这一设计简化了信号量管理,提高了代码的可读性和效率。
EasyLogger源码学习笔记(3)
在EasyLogger源码学习中,枚举变量的使用十分直观。定义枚举类型后,可以直接在代码中操作,提升可读性和代码清晰度。
va_list是一个字符指针,用于在可变参数表中导航和取值。首先,你需要定义一个va_list类型的变量ap,然后通过va_start函数初始化,ap指向变参表的第一个参数,后续的参数获取通过va_arg完成,它会根据指定类型从ap中提取并返回值,同时更新ap的位置。使用完毕后,记得调用va_end来释放ap,以确保程序的健壮性。
对于字符串处理,vsnprintf提供了格式化输出功能,它能在指定长度内限制输出,避免溢出。例如,snprintf函数可以格式化字符串并存储在给定的缓冲区中,确保字符数不超过预设的大小。
在查找字符串时,strstr函数用于在haystack中查找needle首次出现的位置,但不包括结束符。在函数定义中,诸如va_start(args, format)这样的语句用于处理可变参数。
在elog_output函数中,tag_sapce的初始化出现疑问,原因在于需要保证标签对齐,通过memset函数在前面填充空格。这里,用'ELOG_FILTER_TAG_MAX_LEN / 2 - tag_len'的长度来确保足够的空间,而不是'ELOG_FILTER_TAG_MAX_LEN + 1',因为这样可以避免不必要的填充。
在elog_find_tag函数中,返回值的问题在于它实际返回了日志的tag及其后续信息,而不是仅限于tag本身。因此,需要检查并修正这个逻辑,以确保返回正确的内容。
Mobx源码阅读笔记——3. proxy 还是defineProperty,劫持对象行为的两个方案
这篇文章将深入分析 MobX 的 observableObject 数据类型的源码,同时探讨使用 Proxy 和 Object.defineProperty 这两种实现方案来劫持对象行为的策略。通过分析,我们能够理解 MobX 在创建 observableObject 时是如何同时采用这两种方案,并在创建时决定使用哪一种。
首先,回顾 observableArray 的实现方式,通过 Proxy 代理数组的行为,转发给 ObservableArrayAdministration 来实现响应式修改的逻辑。同样,我们已经讨论过 observableValue 的实现,通过一个特殊的类 ObservableValue 直接使用其方法,无需代理。
对于 observableObject 的实现机制,其特点在于同时采用了上述两种方案,并且在创建时决定使用哪一种。让我们回到文章中提到的工厂方法,其中根据 options.proxy 的值来决定使用哪一种方案。
在 options.proxy 为 false 的情况下,使用第一条路径来实现 observableObject。这通过直接返回 extendObservable 的结果,其中 extendObservable 是一个工具函数,用于向已存在的目标对象添加 observable 属性。属性映射中的所有键值对都会导致目标上生成新的 observable 属性,并且属性映射中的任意 getters 会被转化为计算属性。
这里首先根据 options 参数选择特定的 decorator,这个过程与之前在第一篇文章中通过 options 参数选择特定的 enhancer 类似。实际上,这里的 decorator 起到了类似的作用,甚至在创建 decorator 这个过程本身也需要通过 enhancer 参数。
至于 decorator 和 enhancer 之间的耦合机制,文章中详细解释了 createDecoratorForEnhancer 和 createPropDecorator 函数,通过这些函数我们能够了解到它们是如何将 decorator 和 enhancer 联系起来的。
接下来,文章深入分析了 decorator 的作用机制,包括它如何决定是否立即执行,以及在不立即执行时如何将创建 prop 的相关信息保存下来。通过 initializeInstance 函数,我们了解了如何解决 # 问题,这涉及到如何正确处理那些在创建时未被立即执行的 prop。
最终,通过为 target 对象创建 ObservableObjectAdministration 管理对象,并通过 $mobx 和 target 属性将它们关联起来,我们完成了 observableObject 的创建。如果传入的 properties 不为空,则使用 extendObservableObjectWithProperties 来初始化。这里的代码逻辑相对简单,主要遍历 properties 中的所有键并调用对应的 decorator。
文章还指出,虽然在第一条路径中,使用 Object.defineProperty 重写了 prop 的 getter 和 setter,但在 MobX 4 及以下版本中,使用 Proxy 来实现 observableObject 的逻辑更为常见。Proxy 特性在 ES6 引入后,提供了更强大的能力来劫持对象的行为,不仅限于 getter 和 setter,还包括对象的其他行为。
最后,文章总结了使用 Proxy 方案的优点,包括能够更全面地劫持对象的行为,而不仅仅是属性的 getter 和 setter。Proxy 方案在实现双向绑定时,能够提供更灵活和强大的功能。同时,文章也提到了两种方案的局限性,尤其是在处理对象属性的可观察性方面,Proxy 方案在某些情况下可能更具优势。
easylogging源码学习笔记(6)
`LOG` 是默认日志、CLOG自定义日志、LOG_IF条件日志
特殊日志
LOG_EVERY_N、LOG_AFTER_N、LOG_N_TIMES
for (int i = 1; i <= ; ++i) {
LOG_EVERY_N(2, INFO) << "Logged every second iter";
}// 5 logs written; 2, 4, 6, 7,
for (int i = 1; i <= ; ++i) {
LOG_AFTER_N(2, INFO) << "Log after 2 hits; " << i;
}// 8 logs written; 3, 4, 5, 6, 7, 8, 9,
for (int i = 1; i <= ; ++i) {
LOG_N_TIMES(3, INFO) << "Log only 3 times; " << i;
}// 3 logs writter; 1, 2, 3
条件日志和特殊日志可以搭配使用
* `VLOG_IF(condition, verbose-level)`
* `CVLOG_IF(condition, verbose-level, loggerID)`
* `VLOG_EVERY_N(n, verbose-level)`
* `CVLOG_EVERY_N(n, verbose-level, loggerID)`
* `VLOG_AFTER_N(n, verbose-level)`
* `CVLOG_AFTER_N(n, verbose-level, loggerID)`
* `VLOG_N_TIMES(n, verbose-level)`
* `CVLOG_N_TIMES(n, verbose-level, loggerID)`
日志详细等级判定
if (VLOG_IS_ON(2)) {
// Verbosity level 2 is on for this file
}
性能追踪
* `TIMED_FUNC(obj-name)`
* `TIMED_SCOPE(obj-name, block-name)`
* `TIMED_BLOCK(obj-name, block-name)`
这些宏实际上都是关于el::base::type::PerformanceTrackerPtr,一个指向el::base::PerformanceTracker的指针
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
PerformanceTracker::PerformanceTracker(const std::string& blockName,
base::TimestampUnit timestampUnit,
const std::string& loggerId,
bool scopedLog, Level level) :
m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
// We store it locally so that if user happen to change configuration by the end of scope
// or before calling checkpoint, we still depend on state of configuration at time of construction
el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
if (m_enabled) {
base::utils::DateTime::gettimeofday(&m_startTime);
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
}
在构造函数中获取一个时间,
PerformanceTracker::~PerformanceTracker(void) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
if (m_enabled) {
base::threading::ScopedLock scopedLock(lock());
if (m_scopedLog) {
base::utils::DateTime::gettimeofday(&m_endTime);
base::type::string_t formattedTime = getFormattedTimeTaken();
PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
data.init(this);
data.m_formattedTimeTaken = formattedTime;
PerformanceTrackingCallback* callback = nullptr;
for (const std::pair& h
: ELPP->m_performanceTrackingCallbacks) {
callback = h.second.get();
if (callback != nullptr && callback->enabled()) {
callback->handle(&data);
}
}
}
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
}
在析构函数中获取一个时间,处理时间data,使用PerformanceTrackingCallback类型指针callback,并在callback->handle(&data)中处理输出。
由于定义了ELPP_FEATURE_PERFORMANCE_TRACKING,因此在初始化(INITIALIZE_EASYLOGGINGPP)中实际上是安装了一个base::DefaultPerformanceTrackingCallback。
在PerformanceTracker类的handle函数中,callback是一个PerformanceTrackingCallback类型指针,由于安装的是DefaultPerformanceTrackingCallback对象,因此是一个基类指针指向了派生类对象。处理输出的逻辑在DefaultPerformanceTrackingCallback类的handle函数中。
DefaultPerformanceTrackingCallback类的handle函数首先会将数据成员m_data的指针赋值给函数参数,并创建一个base::type::stringstream_t类型的对象ss用于构建输出内容。根据m_data的dataType,输出不同的信息。在输出时,会使用el::base::Writer类构造并输出内容。
Vue Router 源码学习笔记5 - 视图更新的实现
History模块的updateRoute方法主要执行三项关键任务。首先,此方法触发了cb函数,该函数相当于注册监听器,这一过程在VueRouter初始化(src/index.js)阶段完成。其次,更新了内部的_route属性。那么,视图为何会据此更新呢?答案在于响应式属性的机制。
VueRouter的install方法(src/install.js)对全局Vue对象进行了扩展,其中将_route属性定义为响应式属性。这意味着每当_route属性变化,视图就会自动更新。
响应式属性的实现原理基于Object.defineProperty,这是一种JavaScript对象属性的动态绑定机制。通过它,可以为对象属性添加读取和写入操作的监听逻辑,从而实现在属性值改变时触发相应的响应行为。
从设计模式的角度来看,这里采用了发布订阅模式。发布者(即属性值)在变化时发出事件,订阅者(视图)接收到事件后执行相应的更新操作。
进一步探索,可以尝试实现类似Object.defineProperty的功能,以深入理解其工作原理和在Vue中响应式系统中的应用。
太强了!阿里内部传疯了的JDK源码学习笔记,看完才发现差距不止一点点
在闲暇之余,阅读JDK源码能加深对自己开发环境的理解,同时也大有裨益。本文为您介绍阿里巴巴发布的版JDK源码剖析,以展示其内部设计的精妙之处。通过阅读,您将发现与自身知识的差距远超想象。这份详尽的笔记对源码内容进行了精细划分,方便学习。以下是其章节概览:
多线程基础 Atomic类 Lock与Condition 同步工具类 并发容器 线程池与Future ForkJoinPool CompletableFuture请注意,由于笔记内容丰富,篇幅较长,本文仅展示部分章节概览。如有需要,可点击下方链接获取完整版资料。