关键词:
回顾
在上篇博客对GCD的不同的队列继续了底层的源码探索分析, 那么本篇博客将继续对GCD的函数继续源码分析。
1. sync 同步函数
我们都知道
GCD
底层是用C
写的,封装了block
函数来执行添加的任务,那么这个block
底层是如何封装的呢?
dispatch_sync(dispatch_get_main_queue(), ^
NSLog(@"GCD函数分析");
);
在源码里面搜索
dispatch_sync
我们看的是
block
也就是第二个参数work
,直接看work
去哪里了就行,直接定位
在最后一行代码
_dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
搜索
_dispatch_Block_invoke
- 来自一个
dispatch_function_t
类型的,结构体Block_layout *
的invoke
那么现在去看看这 block
的包装函数_dispatch_sync_f
在哪里调用了,通过搜索找到了一个中间层包装,如下代码:
static void
_dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
uintptr_t dc_flags)
_dispatch_sync_f_inline(dq, ctxt, func, dc_flags);
再去搜索_dispatch_sync_f_inline
_dispatch_sync_f_inline
里面if
判断太多了,不知道该看哪个了,改怎么办呢?这个源码是不能编译运行的!
靓仔,不要慌!我们可以在调用函数的地方,下不同的符号断点,看看走哪个😊
- 下符断点
- 运行看看走哪个
通过下符合断点,很清晰的可以看到,走了_dispatch_sync_f_slow
,也就是下面图中代码处:
然后继续在源码里面搜索_dispatch_sync_f_slow
-
_dispatch_sync_f_slow
这又不知道该走哪里,再次下符合断点,发现走到了_dispatch_sync_function_invoke
执行,那么再继续搜索 -
_dispatch_sync_function_invoke
我们找谁使用了这ctxt、func
两个参数,发现是这句代码
_dispatch_client_callout(ctxt, func)
那么现在去搜索一下 -
_dispatch_client_callout
把自己传入返回,和block
的底层是一样的调用方式,这也就说明了在调用_dispatch_client_callout
的时候,异步函数会执行block
,验证如下:
通过断点
在block
函数执行体处,再通过bt
打印调用堆栈
,可以很明显的看到,在_dispatch_client_callout
函数执行后,才会执行block
函数体内。
这一波操作,就很细节,666
,还有谁能把GCD
源码探索的这么清新脱俗,45 度
仰望天空,我这该死的无处安放的魅力!
2. asycn 异步函数
异步也是一样, 那么我们来搜索一波
dispatch_async
还是一样找到work
在哪里使用了,由此定位到qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags)
这句代码,再搜索_dispatch_continuation_init
_dispatch_continuation_init
- 可以发现如下代码,对
work
处理,返回了func
,再传入_dispatch_continuation_init_f
中,继续搜索_dispatch_continuation_init_f
在前面的那个,是直接返回了,但是这里做了一层包装
dc->dc_func = f;
dc->dc_ctxt = ctxt;
包装完了,还有一个对优先级的处理
return _dispatch_continuation_priority_set(dc, dqu, pp, flags);
_dispatch_continuation_priority_set
我们再回到dispatch_async
函数里面
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME;
dispatch_qos_t qos;
qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
这里面就是对执行的任务
设置了优先级
的封装,那么苹果为什么要在异步的里面做这么个封装呢?
- 异步函数代表异步调用
- 异步是无序的调用
- 异步的回调也是异步的,会根据
CPU
的调度在适当的时候异步回调 - 也是函数式编程的一中体现,就是在需要的时候进行回调
但是这里最重要的还是最后一行代码
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
_dispatch_continuation_async
static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
#if DISPATCH_INTROSPECTION
if (!(dc_flags & DC_FLAG_NO_INTROSPECTION))
_dispatch_trace_item_push(dqu, dc);
#else
(void)dc_flags;
#endif
return dx_push(dqu._dq, dc, qos);
这里就会迷失方向了,这个dx_push
是个什么东西呢?搜索了下,找到了下面这个宏定义
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
在宏定义里面dx_vtable
不是我们需要看的,我们去找dq_push
,这个第三个参数 z
就是 qos
,看看dq_push
在哪里使用了
底层为不同类型的队列提供不同的调用入口,比如全局并发队列会调用
_dispatch_root_queue_push
方法。依此作为入口,全局搜索_dispatch_root_queue_push
方法的实现:
前面的代码只是做一些判断封装处理,最终会走到最后一行代码_dispatch_root_queue_push_inline
中,继续跟踪器源码流程:
_dispatch_root_queue_push_inline
继续跟踪_dispatch_root_queue_poke
_dispatch_root_queue_poke
_dispatch_root_queue_poke_slow
这么多代码,看了一遍没有找到什么关键信息,在6295
行代码,_dispatch_root_queues_init
方法里面有关键信息
_dispatch_root_queues_init(void)
dispatch_once_f(&_dispatch_root_queues_pred, NULL,
_dispatch_root_queues_init_once);
这里是一个dispatch_once_f
单例,有个参数_dispatch_root_queues_init_once
,继续搜索
_dispatch_root_queues_init_once
在该方法中进行了线程池的初始化
处理、工作队列
的配置、初始化等工作,这也就是解释了为什么_dispatch_root_queues_init_once是单例的。单例可以避免重复的初始化。
同时这里有一个关键的设置,执行函数的设置,也就是将任务执行的函数被统一设置成了_dispatch_worker_thread2
,如下代码:
cfg.workq_cb = _dispatch_worker_thread2;
r = pthread_workqueue_setup(&cfg, sizeof(cfg));
通过bt
打印程序的运行堆栈信息,来验证异步函数最终任务是通过_dispatch_worker_thread2
调用的
控制台打印的堆栈信息,和我们探索推理的结果是,一模模一样样😁,就问靓仔你服不服!哈哈😁!
3. 总结
GCD
源码难懂,可以通过一些返回值和关键信息推理- 通过 下
符号断点
,追踪代码调用逻辑 - 通过
bt
打印堆栈验证探索结果
更多内容持续更新
🌹 喜欢就点个赞吧👍🌹
🌹 觉得有收获的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹
🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹
ios底层探索之多线程—gcd源码分析(sync同步函数async异步函数)(代码片段)
回顾在上篇博客对GCD的不同的队列继续了底层的源码探索分析,那么本篇博客将继续对GCD的函数继续源码分析。1.sync同步函数我们都知道GCD底层是用C写的,封装了block函数来执行添加的任务,那么这个block底层是如何... 查看详情
ios底层探索之多线程(十五)—@synchronized源码分析(代码片段)
...f;对于锁你又了解多少?锁的原理你又知道吗?iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCDiOS底层探索之多线程(四)—GCD的队列iOS底层探索之多线程(五)—GCD... 查看详情
ios底层探索之多线程—gcd源码分析(栅栏函数)(代码片段)
...顾在上篇博客已经对GCD函数的同步性/异步性还有单例的底层源码,作了详细的分析,那么本篇博客将对栅栏函数,调度组等底层源码进行探索分析!iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)... 查看详情
ios底层探索之多线程—gcd不同队列源码分析(代码片段)
...,那么本篇博客将继续介绍GCD的队列和源码分析。iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCDiOS底层探索之多线程(四)—GCD的队列1.主队列分析查看主队列的api如... 查看详情
ios底层探索之多线程—gcd源码分析(信号量dispatch_semaphore_t)(代码片段)
...栅栏函数做了一个基本介绍,还有应用的举例并且对底层源码进行了分析,本篇博客将对信号量进行探索分析!iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识... 查看详情
ios底层探索之多线程—gcd源码分析(事件源dispatch_source)(代码片段)
...博客已经对GCD的调度组做了介绍和举例应用,还有对底层源码的分析,那么本篇博客将对事件源dispatch_source进行分析!iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁iOS底层探索之多线程(三)... 查看详情
ios底层探索之多线程—gcd源码分析(调度组)(代码片段)
...已经对GCD的信号量做了一个介绍和举例应用,还有对底层源码的分析,那么本篇博客看苹果工程师,如何巧妙封装调度组,看完底层源码直呼好家伙,真是妙啊!!!iOS底层探索之多线程(一)—进程... 查看详情
ios底层探索之多线程(十四)—关于@synchronized锁你了解多少?(代码片段)
...f;对于锁你又了解多少?锁的原理你又知道吗?iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCDiOS底层探索之多线程(四)—GCD的队列iOS底层探索之多线程(五)—GCD... 查看详情
ios底层探索之多线程(十三)—锁的种类你知多少?(代码片段)
...#xff1f;从本篇博客开始将对锁的相关内容进行分析!iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCDiOS底层探索之多线程(四)—GCD的队列iOS底层探索之多线程(五)—GCD... 查看详情
ios底层探索之多线程—gcd的队列(代码片段)
...的认识,那么本篇博客将继续介绍GCD的相关知识。iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCD1.不同队列举例主队列添加同步任务看看下面这个例子🌰//主队... 查看详情
ios底层探索之多线程—初识gcd(代码片段)
...用最多的还是GCD,那么从本篇开始讲陆续介绍GCD。iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁1.什么是GCDGCD定义GrandCenterDispatch简称GCD,是苹果公司开发的技 查看详情
ios底层探索之多线程(十七)——通过swift的foundation源码分析锁(nslocknsconditionnsrecursivelock)(代码片段)
...篇博客就继续分析锁,从Foundation源码分析锁!iOS底层探索之多线程(一)—进程和线程 查看详情
ios底层探索之多线程(十六)——锁分析(nslocknscondtionnsrecursivelocknscondition)(代码片段)
...绍,那么本篇博客就分析一下其他的一些锁!iOS底层探索之多线程(一)— 查看详情
ios开发底层之多线程探索-19(代码片段)
文章目录前言一、进程与线程?1.进程2.线程3.进程与线程的关系二、多线程1.多线程优点2.多线程缺点3.线程的生命周期4.线程池的饱和策略RejectedExecutionHandler接口5.优先级翻转(IOvccpu优先级提升)6.优先级的影响因素三.多线程下... 查看详情
ios开发底层之多线程探索-19(代码片段)
文章目录前言一、进程与线程?1.进程2.线程3.进程与线程的关系二、多线程1.多线程优点2.多线程缺点3.线程的生命周期4.线程池的饱和策略RejectedExecutionHandler接口5.优先级翻转(IOvccpu优先级提升)6.优先级的影响因素三.多线程下... 查看详情
ios底层探索之多线程—进程和线程(代码片段)
前言在iOS的面试中多线程是经常被问到的,多线程也是一个难点,很多面试者平时用的不多,因此很难回答到点子上,那么本篇博客就对多线程进行探索和分析。1.进程和线程什么是进程进程是指在系统中正在运... 查看详情
ios底层探索之多线程(十八)——锁篇章的完结篇(手把手两种方式带你实现一个读写锁!)(代码片段)
...,那么本篇博将手把手带你实现一个读写锁!iOS底层探索之多线程(一)—进程和线程iOS 查看详情
ios开发底层之多线程探索-19(代码片段)
文章目录前言一、进程与线程?1.进程2.线程3.进程与线程的关系二、多线程1.多线程优点2.多线程缺点3.线程的生命周期4.线程池的饱和策略RejectedExecutionHandler接口5.优先级翻转(IOvccpu优先级提升)6.优先级的影响因素三.多线程下... 查看详情