前言
上篇文章对 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:blockfunc: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,在下次进来时,就不会在执行,会直接返回; -
如果在当前任务执行期间,有其他任务进来,会进入无限次等待,原因是当前任务已经获取了锁,进行了加锁,其他任务是无法获取锁的;