准备
主队列分析
dispatch_get_main_queue()
,主队列在main
函数之前就会创建。如下图,在main函数下断点,发现已经有了主队列
打开
libdispatch
源码工程,搜索dispatch_get_main_queue,
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
DISPATCH_GLOBAL_OBJECT
实际上一个宏,传入了一个type,和object参数。可以理解为传入这两个参数后,封装得到一个队列dispatch_queue_main_t
:类型_dispatch_main_q
:队列对象。- 全局搜索
_dispatch_main_q =
,
实际上是一个结构体,并且继承
DISPATCH_GLOBAL_OBJECT_HEADER
,类似objc_class
继承objc_object
串行队列和并行队列
我们通过dispatch_queue_create
方法创建队列,查看源码:会发现调用_dispatch_lane_create_with_target
,代码差不多100多行,抓住重点,主要看函数的返回值。
dispatch_lane_t dq = _dispatch_object_alloc(vtable,sizeof(struct dispatch_lane_s));
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
_dispatch_object_alloc
:开辟内存空间,调用_os_object_alloc_realized
:
_os_object_alloc_realized(**const** **void** *cls, size_t size)
{
_os_object_t obj;
dispatch_assert(size >= **sizeof**(**struct** _os_object_s));
while (unlikely(!(obj = calloc(1u, size)))) {
_dispatch_temporary_resource_shortage();
}
obj->os_obj_isa = cls;
return* obj;
}
调用calloc
开辟空间,同时obj
的 isa
指向vtable
_dispatch_queue_init
:这里面最主要做了队列的初始化,让后通过DQF_WIDTH(width)
,来区分是串行队列和并发队列。width =DISPATCH_QUEUE_WIDTH_MAX
这个宏,表示并非对了,width = 1,表示串行队列。 其中dqai:是dispatch_queue_attr_info_t,实际上就是对优先级做了面向对象的分装。
GCD底层源码继承链
无论是什么队列,最后我们都是以dispatch_queue_t
来接受,顺着dispatch_queue_t
去查看源码:
会通过
DISPATCH_DECL
宏定义:
#define DISPATCH_DECL(name) \
typedef struct name##_s : public dispatch_object_s {} *name##_t
把dispatch_queue
放进去,其中##
是连接符号,编译时候会去掉。
typedef struct dispatch_queue_s : public dispatch_object_s {} *dispatch_queue_t
所以有这样一个继承链接dispatch_queue_t->dispatch_queue_s->dispatch_object_s
。- 实际上深入了解
dispatch_object_s
底层还会继承dispatch_object_t
。dispatch_object_t
底层实际上是一个联合体:
GCD任务调用流程
同步任务
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"123")
});
同步函数的block
是什么时候调用的,下面我们通过源码分析一下
只要通过一条思路,根据
work
参数的传递路线,就可以查到block最终什么时候调用,也就是任务的执行。
dispatch_sync->_dispatch_sync_f->_dispatch_sync_f_inline
_dispatch_sync_f_inline
:发现很多地方都有传入block参数,我们可以新建工程,通过符号断点,一个个去试。dq_width 为1表示串行队列,所以不会走。
下_dispatch_sync_f_slow
符号断点,进入了此方法。
继续符号跟踪
_dispatch_sync_function_invoke
发现调用
_dispatch_client_callout
,这个方法就是block的调用。
同时通过堆栈,也可以证实我们的分析:
异步任务
dispatch_async
源码:
首先通过
_dispatch_continuation_init
将队列和block封装在一起。然后作为参数传入_dispatch_continuation_async
方法。
然后全局搜索
dx_push
方法
vtable实际上就是通过队列封装的类,然后搜索
dq_push
,发现不同的队列,通过不同的方法实现
下面我们以全局并发队列开始继续跟踪:
_dispatch_root_queue_push
,会调用_dispatch_root_queue_push_inline
继续跟踪:_dispatch_root_queue_push_inline->_dispatch_root_queue_poke_slow
发现会有个一个_dispatch_root_queues_init
,这个里面会有一个单列_dispatch_root_queues_init_once
,进行初始化
- 线程池初始化:
_dispatch_root_queue_init_pthread_pool
- 工作队列的配置:
pthread_workqueue_config
- 执行函数的设置:cfg.workq_cb =
_dispatch_worker_thread2
通过bt打印也证实了这一点:
_dispatch_worker_thread2
传给系统方法,最后通过系统cpu来调度。