前言
上篇文章对 GCD
在 OC
层的使用做了详细的探索,那么这篇文章将对 GCD
的底层进行探索。
资源准备
主队列的底层分析
主队列是GCD
提供的特殊的串行队列,在libdispatch
源码中,找到主队列的定义,找到它是串行队列的依据
主队列的实现类型
有两种实现类型:
类型一
通过 dispatch_get_main_queue
这种方法实现,我们可以在libdispatch
源码中进行搜索,源码如下:
其中参数1
:dispatch_queue_main_t
,这个是个结构体。
而 dispatch_queue_static_s
结构体的实现如下:
参数2
: _dispatch_main_q
为队列类型,在源码中搜索_dispatch_main_q
,找到的结果非常多,这时可以尝试搜索_dispatch_main_q =
,如果是赋值操作,可以缩小结果的范围,通过搜索,看底层实现:
类型二
在代码中,创建主队列,使用lldb
,执行命令 po queue
, 可以输出它的结构,其中包含队列标识符。打印出结果后,可以知道和主队列关联的com.apple.main-thread
,可以把这个作为关键词在libdispatch
源码中进行搜索。就能直接找到上图_dispatch_main_q =
的结果。
主队列是串行队列的探索
在刚刚主队列的底层探索中,可以知道,主队列的dq_atomic_flags
为DQF_THREAD_BOUND | DQF_WIDTH(1)
;那么我们可以通过找到串行队列的dq_atomic_flags
与主队列的进行对比,如果一致,即可证明主队列就是一个串行队列。
创建串行队列,使用的是:dispatch_queue_create
,那么在libdispatch
源码中搜索这个关键词:
其中返回的是 _dispatch_lane_create_with_target
函数,那么接着进入这个函数里面,查看实现情况:
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa, dispatch_queue_t tq, bool legacy) {
// 面向对象的包装
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
// Step 1: Normalize arguments (qos, overcommit, tq)
// 前期的准备工作,优先级处理
...
// Step 2: Initialize the queue
// 初始化队列
const void *vtable;
dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
if (dqai.dqai_concurrent) {
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
vtable = DISPATCH_VTABLE(queue_serial);
}
// 申请开辟内存
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));
dq->dq_label = label;
return _dispatch_trace_queue_create(dq)._dq;
}
-
通过
_dispatch_queue_attr_to_info
对dqa
进行面向对象的包装,为dqai
。 -
_dispatch_queue_init
是构造函数,看它的第三个参数,当是concurrent
的时候传DISPATCH_QUEUE_WIDTH_MAX
,否则传1
。
进入_dispatch_queue_init
函数,找到参数3
:
如果是串行队列,传入的width
为1
,dqf
为DQF_WIDTH(1)
,和主队列dq_atomic_flags
属性设置的值一致,所以主队列就是一个串行队列。
在这个函数里面,还有 dq_serialnum
这参数,是用来做什么的了?
dq_serialnum
的作用
有一些说法,如果dq_serialnum
设置为1
,就是串行队列,例如:主队列的设置
在libdispatch
源码中,搜索dq_serialnum
,可以找到很多赋值的场景
查看 DISPATCH_QUEUE_SERIAL_NUMBER_WLF
的定义:
同时找到了dq_serialnum
的相关注释
-
跳过零
-
主队列:
1
-
mgr_q
:2
-
mgr_root_q
:3
-
全局队列:
4~15
-
自定义队列:
17
所以dq_serialnum
并不是用来标记串行和并发队列的。
自定义队列的dq_serialnum
从上面的分析,我们知道自定义队列是 17
:
#define DISPATCH_QUEUE_SERIAL_NUMBER_INIT 17
搜索DISPATCH_QUEUE_SERIAL_NUMBER_INIT
,找到被赋值的代码:
那么就接着探索 _dispatch_queue_serial_numbers
:
再探索下 os_atomic_inc_orig
这个宏定义:
#define os_atomic_inc_orig(p, m) \
os_atomic_add_orig((p), 1, m)
#define os_atomic_add_orig(p, v, m) \
_os_atomic_c11_op_orig((p), (v), m, add, +)
#define _os_atomic_c11_op_orig(p, v, m, o, op) \
atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), v, \
memory_order_##m)
-
atomic_fetch_##o##_explicit
中的##o##
为占位,替换的o
传入的值为add
-
替换后为
atomic_fetch_add_explicit
,来自C++11
的原子操作函数 -
宏的作用,传入
17
,之后每次+1
。
全局队列分析
我们先探究下,我们在使用全局队列时,能通过 lldb
打印出什么?
打印出结果之后,我们就可以在libdispatch
源码里面进行探索了,以 com.apple.root.default-qos
为关键词搜索:
struct dispatch_queue_global_s _dispatch_root_queues[] = {
#define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \
((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \
DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \
DISPATCH_ROOT_QUEUE_IDX_##n##_QOS)
#define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
[_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global), \
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
.do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
.dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
_dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
_dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
__VA_ARGS__ \
}
_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,
.dq_label = "com.apple.root.maintenance-qos",
.dq_serialnum = 4,
),
_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.maintenance-qos.overcommit",
.dq_serialnum = 5,
),
_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,
.dq_label = "com.apple.root.background-qos",
.dq_serialnum = 6,
),
_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.background-qos.overcommit",
.dq_serialnum = 7,
),
_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,
.dq_label = "com.apple.root.utility-qos",
.dq_serialnum = 8,
),
_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.utility-qos.overcommit",
.dq_serialnum = 9,
),
//✅ com.apple.root.default-qos
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
.dq_label = "com.apple.root.default-qos",
.dq_serialnum = 10,
),
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.default-qos.overcommit",
.dq_serialnum = 11,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,
.dq_label = "com.apple.root.user-initiated-qos",
.dq_serialnum = 12,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-initiated-qos.overcommit",
.dq_serialnum = 13,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
.dq_label = "com.apple.root.user-interactive-qos",
.dq_serialnum = 14,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-interactive-qos.overcommit",
.dq_serialnum = 15,
),
};
-
_dispatch_root_queues
为数组类型,默认将4~15
的全局队列全部初始化,获取时通过不同类型,获取不同的队列。 -
和主队列的类型差异:
-
- 主队列:
dispatch_queue_static_s
;
- 主队列:
-
- 全局队列:
dispatch_queue_global_s
。
- 全局队列:
创建队列
搜索dispatch_queue_create
,进入_dispatch_lane_create_with_target
函数:
DISPATCH_NOINLINE
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa, dispatch_queue_t tq, bool legacy) {
// 1、创建dqai
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
// 2、规范化参数,例如:qos, overcommit, tq
dispatch_qos_t qos = dqai.dqai_qos;
...
// 3、根据队列类型,创建vtable
if (legacy) {
// if any of these attributes is specified, use non legacy classes
if (dqai.dqai_inactive || dqai.dqai_autorelease_frequency) {
legacy = false;
}
}
const void *vtable;
dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
if (dqai.dqai_concurrent) {
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
vtable = DISPATCH_VTABLE(queue_serial);
}
...
// 4、开辟空间,初始化队列
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));
dq->dq_label = label;
...
// 5、根据不同类型,通过模板创建队列
dq->do_targetq = tq;
_dispatch_object_debug(dq, "%s", __func__);
return _dispatch_trace_queue_create(dq)._dq;
}
创建dqai
在源码里面搜索_dispatch_queue_attr_to_info
,并且进入该函数:
-
创建空的
dqai
,默认为串行队列。如果传入的参数为空,返回空dqai
; -
传入的参数有值,将队列信息保存到
dqai
中
规范化参数
规范化参数,例如:qos
, overcommit
, tq
...
- 设置优先级、服务质量等参数。
创建vtable
根据队列类型,创建 vtable
。
通过DISPATCH_VTABLE
,将传入的队列类型拼接vtable
。
再查看下DISPATCH_VTABLE
的宏定义:
#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
#define DISPATCH_OBJC_CLASS(name) (&DISPATCH_CLASS_SYMBOL(name))
#define DISPATCH_CLASS_SYMBOL(name) _dispatch_##name##_vtable
初始化队列
①、使用_dispatch_object_alloc
函数开辟空间:
再进入_os_object_alloc_realized
函数:
- 包含
isa
指向,说明队列也是一个对象。
②、使用_dispatch_queue_init
函数初始化队列,进入函数里面查看其实现:
- 队列类型为
dispatch_queue_t
,为队列的成员变量赋值.
通过模板创建队列
使用_dispatch_introspection_queue_create
函数,传入初始化后的dq
流程:_dispatch_introspection_queue_create
→_dispatch_introspection_queue_create_hook
→dispatch_introspection_queue_get_info
→_dispatch_introspection_lane_get_info
继承链
使用_dispatch_lane_create_with_target
创建队列,返回dispatch_queue_t
类型。
dispatch_queue_t
类型来自于宏定义
DISPATCH_DECL(dispatch_queue);
#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)
#define OS_OBJECT_DECL_SUBCLASS(name, super) \
OS_OBJECT_DECL_IMPL(name, NSObject, <OS_OBJECT_CLASS(super)>)
#define OS_OBJECT_DECL_IMPL(name, adhere, ...) \
OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \
typedef adhere<OS_OBJECT_CLASS(name)> \ * OS_OBJC_INDEPENDENT_CLASS name##_t
#define OS_OBJECT_DECL_PROTOCOL(name, ...) \
@protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \ @end
#define OS_OBJECT_CLASS(name) OS_##name
-
第一步:使用
OS_OBJECT_CLASS
在前面拼接OS_
,例如:OS_dispatch_queue
; -
第二步:使用
OS_OBJECT_DECL_PROTOCOL
在结尾拼接_t
,例如:OS_dispatch_queue_t
; -
第三步:使用泛型接收,
typedef adhere<OS_dispatch_queue> * OS_OBJC_INDEPENDENT_CLASS OS_dispatch_queue_t
;
DISPATCH_DECL
宏在dispatch_object_s
结构体中的定义:
-
那么把参数带入进去,就能得到:
typedef struct dispatch_queue_s : public dispatch_object_s {} *dispatch_queue_t
-
相当于:
dispatch_queue_t
的本质是dispatch_queue_s
,而dispatch_queue_s
继承自dispatch_object_s
。
dispatch_object_s
来自于dispatch_object_t
的联合体:
-
而
dispatch_object_t
可以代表联合体里面的所有的类型,所以最终的根类是dispatch_object_t
。 -
最终得到的继承链:
dispatch_queue_t
-->dispatch_queue_s
-->dispatch_object_s
-->dispatch_object_t
。
现在我们探究的是dispatch_queue_t
,所以要看其本质,也就是dispatch_queue_s
。找到dispatch_queue_s
的结构:
再看下结构体内嵌套的DISPATCH_QUEUE_CLASS_HEADER
定义:
#define DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
_DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__); \
/* LP64 global queue cacheline boundary */ \
unsigned long dq_serialnum; \
const char *dq_label; \
DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, \
const uint16_t dq_width, \
const uint16_t __dq_opaque2 \
); \
dispatch_priority_t dq_priority; \
union { \
struct dispatch_queue_specific_head_s *dq_specific_head; \
struct dispatch_source_refs_s *ds_refs; \
struct dispatch_timer_source_refs_s *ds_timer_refs; \
struct dispatch_mach_recv_refs_s *dm_recv_refs; \
struct dispatch_channel_callbacks_s const *dch_callbacks; \
};\
int volatile dq_sref_cnt
struct dispatch_queue_s {
DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
/* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;
struct dispatch_workloop_s {
struct dispatch_queue_s _as_dq[0];
DISPATCH_QUEUE_CLASS_HEADER(workloop, dispatch_timer_heap_t dwl_timer_heap);
uint8_t dwl_drained_qos;
/* 24 bits hole */
struct dispatch_object_s *dwl_heads[DISPATCH_QOS_NBUCKETS];
struct dispatch_object_s *dwl_tails[DISPATCH_QOS_NBUCKETS];
dispatch_workloop_attr_t dwl_attr;
} DISPATCH_ATOMIC64_ALIGN;
#define DISPATCH_LANE_CLASS_HEADER(x) \
struct dispatch_queue_s _as_dq[0]; \
DISPATCH_QUEUE_CLASS_HEADER(x, \
struct dispatch_object_s *volatile dq_items_tail); \
dispatch_unfair_lock_s dq_sidelock; \
struct dispatch_object_s *volatile dq_items_head; \
uint32_t dq_side_suspend_cnt
typedef struct dispatch_lane_s {
DISPATCH_LANE_CLASS_HEADER(lane);
/* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN *dispatch_lane_t;
// Cache aligned type for static queues (main queue, manager)
struct dispatch_queue_static_s {
struct dispatch_lane_s _as_dl[0]; \
DISPATCH_LANE_CLASS_HEADER(lane);
} DISPATCH_CACHELINE_ALIGN;
#define DISPATCH_QUEUE_ROOT_CLASS_HEADER(x) \
struct dispatch_queue_s _as_dq[0]; \
DISPATCH_QUEUE_CLASS_HEADER(x, \
struct dispatch_object_s *volatile dq_items_tail); \
int volatile dgq_thread_pool_size; \
struct dispatch_object_s *volatile dq_items_head; \
int volatile dgq_pending
struct dispatch_queue_global_s {
DISPATCH_QUEUE_ROOT_CLASS_HEADER(lane);
} DISPATCH_CACHELINE_ALIGN;
- 在
DISPATCH_OBJECT_HEADER
中包含dispatch_object_s
结构体和_DISPATCH_OBJECT_HEADER
查看_DISPATCH_OBJECT_HEADER
的定义:
- 而在
_DISPATCH_OBJECT_HEADER
中又包含_os_object_s
结构体和OS_OBJECT_STRUCT_HEADER
找到并查看OS_OBJECT_STRUCT_HEADER
的定义:
#define OS_OBJECT_STRUCT_HEADER(x) \
_OS_OBJECT_HEADER(\
const struct x##_vtable_s *__ptrauth_objc_isa_pointer do_vtable, \
do_ref_cnt, \
do_xref_cnt)
再通过 _OS_OBJECT_HEADER
查看其定义:
#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \
isa; /* must be pointer-sized and use __ptrauth_objc_isa_pointer */ \
int volatile ref_cnt; \
int volatile xref_cnt
- 发现包含
isa
成员变量。
所以dispatch_queue_s
结构体还伪继承于_os_object_s
,并包含isa
成员变量,说明队列也是一个对象。
同步函数
全局队列
同步函数配合全局队列
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"Block");
});
在libdispatch
源码中,找到dispatch_sync
函数的实现:
-
传入的
work
参数就是任务的block
,只需要一直追寻work
参数就能找到调用的代码; -
传入
_dispatch_sync_f
函数的参数3
,对work
进行包装。
接着看下 参数3
的 实现情况,进入到_dispatch_Block_invoke
里面查定义:
- 从定义里面,我们可以知道,是将
block
下的invoke
强转为dispatch_function_t
结构体。
分析完参数,那么接着再看下 _dispatch_sync_f
函数的实现:
结合上面,再一一对应函数里面的参数:
ctxt
:block
func
:invoke
接着往下找,进入_dispatch_sync_f_inline
函数:
从源码里面看,函数中出现了复杂的逻辑,可以使用符号断点,找到正确的逻辑分支。
就可以定位到_dispatch_sync_f_slow
函数,进入这个函数里面:
同样是复杂逻辑,继续使用符号断点。
继续在项目中,将所有可能的条件分支,全部设置符号断点:
- 进入
_dispatch_sync_function_invoke
函数
进入_dispatch_sync_function_invoke
函数:
接着,再进入_dispatch_sync_function_invoke_inline
函数:
到了这里,就很明白了:
-
_dispatch_thread_frame_push
:任务加入队列 -
_dispatch_client_callout
:执行任务 -
_dispatch_thread_frame_pop
:任务移除队列
进入_dispatch_client_callout
函数
- 调用
f(ctxt)
就是内部自动调用block
任务的代码。
我们再通过lldb
进行验证:
- 和源码中的逻辑完全一致
同步函数顺序被执行,因为_dispatch_sync_function_invoke_inline
中,三个函数的顺序调用:
-
_dispatch_thread_frame_push
:任务加入队列 -
_dispatch_client_callout
:执行任务 -
_dispatch_thread_frame_pop
:任务移除队列
当同步函数加入串行队列,在_dispatch_sync_f_inline
函数中,使用_dispatch_barrier_sync_f
同步栅栏函数。
并发队列
同步函数配合并发队列:
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"block---:%@",[NSThread currentThread]);
});
我们刚刚在前面分析的,dispatch_sync
--> _dispatch_sync_f
,再根据_dispatch_sync_f
的实现:
再进入 _dispatch_sync_f_inline
函数里面查看详情:
并发队列触发的代码分支和全局队列有所不同。
进入_dispatch_sync_invoke_and_complete
函数:
- 最终调用的还是
_dispatch_sync_function_invoke_inline
函数。
函数中的最后一个入参
dispatch_function_t func DISPATCH_TRACE_ARG(void *dc)
#define DISPATCH_TRACE_ARG(arg)
#define DISPATCH_TRACE_ARG(arg) , arg
- 将
,
和arg
封装到一起,所以外部不用加额外的逗号。
死锁
- 当出现死锁异常时,调用
_dispatch_sync_f_slow
函数 - 然后调用
__DISPATCH_WAIT_FOR_QUEUE__
,抛出异常
根据前面几个情况的分析,探索流程:dispatch_sync
--> _dispatch_sync_f
--> _dispatch_sync_f_inline
--> _dispatch_sync_f_inline
.
进入_dispatch_sync_f_inline
函数:
当前为同步函数,并且加入串行队列,调用_dispatch_barrier_sync_f
函数,同步函数加入串行队列的底层,使用同步栅栏函数,进入_dispatch_barrier_sync_f
函数:
接着进入_dispatch_barrier_sync_f_inline
函数:
- 出现死锁异常时,已知的
_dispatch_sync_f_slow
函数
再进入_dispatch_sync_f_slow
函数:
- 内部调用
__DISPATCH_WAIT_FOR_QUEUE__
抛出异常。
进入__DISPATCH_WAIT_FOR_QUEUE__
函数:
_dq_state_drain_locked_by
函数返回真,抛出异常- 传入两个参数,
dq_state
为队列状态,dsc_waiter
为线程的tid
进入_dq_state_drain_locked_by
函数:
再进入_dispatch_lock_is_locked_by
函数:
- 找到判断依据
DLOCK_OWNER_MASK
。
再查看DLOCK_OWNER_MASK
定义情况:
-
DLOCK_OWNER_MASK
定位为很大值,只要一个非零的值和它进行&
运算,结果一定不为零; -
lock_value
和tid
进行按位异或,它们运算结果为零只有一种可能,就是二者的值相同;
当同步函数加入到串行队列中,同一线程在等待时又被执行,就会形成相互等待的局面,造成死锁的异常。
异步函数
在libdispatch
源码中,找到dispatch_async
函数的实现:
- 将任务封装成
dispatch_qos_t
类型的qos
- 调用
_dispatch_continuation_async
函数
任务和优先级的封装
进入_dispatch_continuation_init
函数
进入_dispatch_continuation_init_f
函数,对block
任务进行封装:
进入_dispatch_continuation_priority_set
函数,对任务优先级进行封装:
异步函数的执行,优先级是衡量标准的其中一项。而且任务根据CPU
的调度情况异步执行,所以一定是无序的,需要对其进行封装。
并发队列
异步函数配合并发队列
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"block---:%@",[NSThread currentThread]);
});
根据前面的分析: dispatch_async
--> _dispatch_continuation_async
。再进入 _dispatch_continuation_async
里面,查看实现:
接着再查看dx_push
的宏定义:
-
此时我们关注的是
参数3
的调用,可以暂时无视dx_vtable
,继续跟踪dq_push
; -
dq_push
属于函数的赋值点,根据队列类型的不同赋值也不同。
查看并发队列的赋值:
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
.do_type = DISPATCH_QUEUE_CONCURRENT_TYPE,
.do_dispose = _dispatch_lane_dispose,
.do_debug = _dispatch_queue_debug,
.do_invoke = _dispatch_lane_invoke,
.dq_activate = _dispatch_lane_activate,
.dq_wakeup = _dispatch_lane_wakeup,
.dq_push = _dispatch_lane_concurrent_push,
);
进入_dispatch_lane_concurrent_push
函数:
使用符号断点,先进入_dispatch_continuation_redirect_push
函数:
再次调用dx_push
,此时队列类型为queue_pthread_root
,所以dx_push
不是_dispatch_lane_concurrent_push
函数,而是对应了_dispatch_root_queue_push
函数,类似于调用了父类的方法:
通过符号断点配合汇编代码查看流程:
_dispatch_root_queue_push
--> _dispatch_root_queue_push_override
--> _dispatch_root_queue_poke
--> _dispatch_root_queue_poke_slow
.
进入_dispatch_root_queue_poke_slow
函数:
static void _dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor) {
...
_dispatch_root_queues_init();
...
do {
_dispatch_retain(dq);// released in _dispatch_worker_thread
while ((r = pthread_create(pthr, attr, _dispatch_worker_thread, dq))) {
if (r != EAGAIN) {
(void)dispatch_assume_zero(r);
}
_dispatch_temporary_resource_shortage();
}
}
while (--remaining);
...
- 通过
_dispatch_root_queues_init
注册异步任务执行的回调; - 通过
do...while
循环创建线程,使用pthread_create
函数。
当然,我们也可以通过 lldb
调试,打印 堆栈信息,来进行反推:
- 由系统的
_pthread_wqthread
调用libdispatch
的_dispatch_worker_thread2
函数。
源码中正向推导流程:
_dispatch_root_queues_init
--> _dispatch_root_queues_init_once
--> _dispatch_worker_thread2
对_dispatch_worker_thread2
进行赋值,封装给pthread
的API
调用:
- 异步线程被
CPU
调度,系统在合适的时机,通过libsystem_pthread.dylib
的_pthread_wqthread
函数,调用libdispatch.dylib
的_dispatch_worker_thread2
函数,最终执行异步函数的任务
异步任务回调流程:
_dispatch_worker_thread2
--> _dispatch_root_queue_drain
--> _dispatch_async_redirect_invoke
--> _dispatch_continuation_pop
--> _dispatch_client_callout
--> _dispatch_call_block_and_release
异步函数配合并发队列的逻辑:
-
首先对任务和优先级进行封装;
-
多次调用dx_push,最终找到
_dispatch_root_queue_push
; -
通过
_dispatch_root_queues_init
注册异步任务执行的回调; -
通过
do...while
循环创建线程,使用pthread_create
函数; -
异步线程被
CPU
调度,系统在合适的时机,通过libsystem_pthread.dylib
调用_dispatch_worker_thread2
函数,执行回调。
全局队列
异步函数配全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"block---:%@",[NSThread currentThread]);
});
代码逻辑:dispatch_async
--> _dispatch_continuation_async
--> _dispatch_continuation_async
--> dx_push
。
此时调用的dx_push
,和并发队列的赋值不同
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
.do_type = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
.do_dispose = _dispatch_object_no_dispose,
.do_debug = _dispatch_queue_debug,
.do_invoke = _dispatch_object_no_invoke,
.dq_activate = _dispatch_queue_no_activate,
.dq_wakeup = _dispatch_root_queue_wakeup,
.dq_push = _dispatch_root_queue_push,
);
进入_dispatch_root_queue_push
函数流程:
_dispatch_root_queue_push
--> _dispatch_root_queue_push_override
--> _dispatch_root_queue_push_inline
--> _dispatch_root_queue_poke
--> _dispatch_root_queue_poke_slow
进入_dispatch_root_queue_poke_slow
函数:
- 通过
_dispatch_root_queues_init
注册异步任务执行的回调 - 通过
do...while
循环创建线程,使用pthread_create
函数
使用lldb
进行反推:
异步任务回调流程:
_dispatch_worker_thread2
--> _dispatch_root_queue_drain
--> _dispatch_queue_override_invoke
--> _dispatch_client_callout
--> _dispatch_call_block_and_release
单例模式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"block---:%@",[NSThread currentThread]);
});
来到源码,找到dispatch_once
函数的实现:
进入dispatch_once_f
函数:
-
将
val
强转为dispatch_once_gate_t
类型,类似于栅栏的使用 三个条件分支: -
如果执行完成,直接返回
-
如果第一次,执行
_dispatch_once_callout
函数 -
如果正在执行,进入
_dispatch_once_wait
等待
锁的处理
进入_dispatch_once_gate_tryenter
函数:
- 进行原子锁的处理,防止多线程
执行任务
进入_dispatch_once_callout
函数:
再进入 _dispatch_client_callout
函数:
- 通过
f(ctxt)
执行任务的回调。
接着再看 _dispatch_once_callout
函数里面调用的_dispatch_once_gate_broadcast
函数:
- 锁的处理,并标记为完成
单例模式的原理:
-
调用
dispatch_once
函数,传入onceToken
和block
。其中onceToken
为静态变量,具有唯一性,在底层被强转为dispatch_once_gate_t
类型的变量l
,l
通过os_atomic_load
函数获取底层原子封装性的关联,得到变量v
,通过v
来查询任务的状态,如果此时v
等于DLOCK_ONCE_DONE
,说明任务已经处理过一次了,直接return
; -
如果任务首次执行,将任务进行加锁,任务状态置为
DLOCK_ONCE_UNLOCK
,目的保证线程安全。加锁后进行block
回调函数的执行,执行完成后,将当前任务解锁,将当前的任务状态置为DLOCK_ONCE_DONE
,在下次进来时,就不会在执行,会直接返回; -
如果在当前任务执行期间,有其他任务进来,会进入无限次等待,原因是当前任务已经获取了锁,进行了加锁,其他任务是无法获取锁的;