皮皮网

【按键宏源码】【酒鬼回家游戏源码】【长春自助建站源码】cfrunloop 源码

2024-11-19 00:46:42 来源:鸿蒙源码开源时间

1.OC内存管理-runloop
2.Chromium setTimeout/clearTimeout 源码分析

cfrunloop 源码

OC内存管理-runloop

        RunLoop 是通过内部维护的 事件循环( Event Loop )来对 事件/消息进行管理的一个对象。

        runloop 的官方文档在 thread 篇章 Run Loops ,也就从侧面说明了 runloop 是与线程息息相关的。

        官方有如下一张图:

        线程的输入源:

        线程针对输入源的处理机制:

        有以下案例:

        timer 与 performSelector 对应的回调都是 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ :

        block 对应 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ :

        主线程对应 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ :

        系统触摸事件对应 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ :

        通知事件对应 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ :

        小结:

        滚动页面输出:

        页面滚动过程中处于 UITrackingRunLoopMode ,静止状态处于 kCFRunLoopDefaultMode 。

        输出:

        输出:

        既然 runloop 是一个事件循环,那么它与普通的循环有什么区别呢?

        普通循环:

        runloop 循环:

        那么可以得到以下结论:

        那么 runloop 是怎么做到的呢?

        通常我们会通过 NSRunLoop 去获取当前的 runloop :

        定义如下:

        给 currentRunLoop 下符号断点:

        通过之前的分析已经定位到了 runloop 是在 CoreFoundation 中的 CoreFoundation源码 。正好 CoreFoundation 开源了 CFRunLoop :

        那么核心逻辑就在 CFRunLoopRunSpecific 中。还有一个疑问是 runloop 可以休眠,那么它是如何实现的呢?

        要了解 runloop 的实现原理,首先要清楚它的数据结构。

        CFRunLoopRunSpecific 的第一个参数是 CFRunLoopGetCurrent() :

        _CFRunLoopGet0

        CFRunLoopRef 的定义如下:

        实际上底层它是 __CFRunLoop 类型:

        对于 timer 而言:

        显然它是要依赖 mode 的。

        CFRunLoopMode

        而一个 mode 下又对应多个 items(source0、source1、timers、observers) ,所以就有如下关系:

        既然有多种 mode ,那么都有哪些呢?

        源码中有如下定义:

        它们对应 Foundation 中的:

        我们都清楚在页面滚动的时候有一个 UITrackingRunLoopMode :

        除了以上 3 种 mode 还有两个私有 mode :

        当 RunLoop 运行在 Mode1 上时,是无法接受处理 Mode2 或 Mode3 上的 Source、Timer、Observer 事件的。

        以 timer 为例,将 timer 加入到 runloop 中:

        底层调用了 CFRunLoopAddTimer :

        根据要加入的 mode 区分是 common mode 和非 common mode 将 timer 加入 mode 中。这个时候只是将 timer 加入了 mode 中,要执行肯定要调用 CFRunLoopRun ,最终要调用 CFRunLoopRunSpecific 。

        在 __CFRunLoopRun 中调用了 __CFRunLoopDoTimers :

        找到 mode 中的所有 timer 然后调用 __CFRunLoopDoTimer 。

        CFRunLoopAddTimer -> CFRunLoopRunSpecific -> __CFRunLoopRun -> __CFRunLoopDoTimers -> __CFRunLoopDoTimer -> __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ 。

        与 timer 相同 source 会调用 CFRunLoopAddSource :

        CFRunLoopAddSource -> CFRunLoopRunSpecific -> __CFRunLoopRun -> __CFRunLoopDoSources0/__CFRunLoopDoSources1 -> __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ /__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__

        同理 observer 会调用 CFRunLoopAddObserver 。

Chromium setTimeout/clearTimeout 源码分析

       Chromium版本.0..3中setTimeout函数的工作流程涉及大量源码,包括线程、消息循环、任务队列和操作系统定时器函数。按键宏源码本文仅分析setTimeout的关键步骤。

       setTimeout函数通过创建包含回调函数和延时时间的action对象,调用DOMTimer::Install进行处理。DOMTimer::Install通过DOMTimerCoordinator::InstallNewTimeout向定时器哈希表timers_插入一个定时器对象,生成唯一timeout_id。

       timeout_id由NextID生成,每次调用setTimeout返回递增的酒鬼回家游戏源码值,用于唯一标识每个定时器任务。timers_是一个哈希表,存放定时器对象,与任务一一对应。

       创建定时器对象时,长春自助建站源码通过定时器的延时时间获取任务类型,并将回调函数与任务类型关联,最终通过web_task_runner_获取相应的任务运行器,并在TimerBase::SetNextFireTime调用web_task_runner_->PostDelayedTask提交延迟任务。

       PostDelayedTask将延迟任务插入到延迟任务队列中,轻校系统源码并更新当前线程的唤醒时间。延迟任务队列是优先队列,用于管理按延时时间排序的任务。

       通过GetNextScheduledWakeUpImpl获取优先队列的队头任务,创建唤醒任务用于在线程唤醒时执行延迟任务。卡通html表白源码唤醒任务只包含延时时间,不包含回调函数。

       UpdateDelayedWakeUpImpl根据新创建的唤醒任务更新唤醒任务队列。如果延迟任务队列中的任务延时时间较短,新任务可能无法立即进入唤醒任务队列。

       调用操作系统定时器函数,如在Mac下调用CFRunLoopTimerSetNextFireDate,在Windows下调用SetTimer,在Android下调用timerfd_settime,在指定延时后唤醒线程。

       线程睡眠后,唤醒线程执行已到期的延迟任务,将到期任务从延迟任务队列移出并加入工作队列。ThreadControllerWithMessagePumpImpl::DoWorkImpl找到并执行工作队列中的任务。

       面试题:setTimeout延迟时间不准确的原因可能有:硬件层面的时间不准确、操作系统不保证定时器函数的精确性、CPU处理大量定时任务时可能出现部分任务延迟执行。

       clearTimeout与clearInterval功能相同,DOMTimer::RemoveByID从timers_哈希表中移除指定timeout_id对应的定时器对象,将回调函数置空,视为任务取消。