看了快半个月的GCD源码,只能说太变态了。
先来吐槽一下:一个函数,调用栈都是十几层…… 为了效率,代码使用了纯C语言,但是为了模拟面向对象中的继承,虚函数等,定义了一层层的宏定义,看一个struct的定义要绕过来绕过去…… 网上的资料极少,有的那几篇,还都是用旧版本的GCD在说事儿,而新版的GCD源码复杂度及晦涩度,在原有的基础上,又升级了一个档次……
说实话,到现在也不敢说是看懂了或是看对了,所以这篇博客只是个人的一个不负责任的总结而已。天哪,让我从GCD的泥潭中出来吧……
GCD的类都是struct定义的。但并不像runtime一样,直接使用:
来继承定义。而是将所有的父类的数据成员,都平铺重复的写在一个个的struct中,这样做的好处应该是为了提高效率,避免引入继承机制带来的代码执行上的延迟?但是,为了减少代码量和易读性,苹果还是很“贴心”的做了许多宏定义,在阅读源码时,你不得不将这些宏,替换为它真实的定义。
GCD中的数据结构是如下组织的:
GCD中类都继承自统一的基类dispatch_object_t
, 再下一层,则是表示系统对象的基类_os_object_s
。
root 基类dispatch_object_t
的定义如下:
typedef union {
struct _os_object_s *_os_obj;
struct dispatch_object_s *_do;
struct dispatch_continuation_s *_dc;
struct dispatch_queue_s *_dq;
struct dispatch_queue_attr_s *_dqa;
struct dispatch_group_s *_dg;
struct dispatch_source_s *_ds;
struct dispatch_mach_s *_dm;
struct dispatch_mach_msg_s *_dmsg;
struct dispatch_source_attr_s *_dsa;
struct dispatch_semaphore_s *_dsema;
struct dispatch_data_s *_ddata;
struct dispatch_io_s *_dchannel;
struct dispatch_operation_s *_doperation;
struct dispatch_disk_s *_ddisk;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;
可见它是一个联合,可以表示union中任意的类型。起到的作用和基类指针相似,所有的子类型都可以用dispatch_object_t
统一表示。
dispatch_queue_s
: GCD中的queue。
dispatch_continuation_s
: 我们向queue提交的任务,无论block还是function形式,最终都会被封装为dispatch_continuation_s。
注意,这只是逻辑上的继承关系,在实际的源码中,是看不到继承的符号的。而是通过宏的方式,将父类中的属性,在子类中再写一次。如继承了_os_object_s
的dispatch_object_s
的定义:
struct dispatch_object_s {
_DISPATCH_OBJECT_HEADER(object);
};
#define _DISPATCH_OBJECT_HEADER(x) \
struct _os_object_s _as_os_obj[0]; \
OS_OBJECT_STRUCT_HEADER(dispatch_##x); \ // 这个宏,可以理解为dispatch_object_s继承自_os_object_s
struct dispatch_##x##_s *volatile do_next; \
struct dispatch_queue_s *do_targetq; \
void *do_ctxt; \
void *do_finalizer
简单的理解了GCD中基本类的关系,我们就来看一下各个类中的定义。
typedef struct _os_object_s {
_OS_OBJECT_HEADER(
const _os_object_vtable_s *os_obj_isa,
os_obj_ref_cnt,
os_obj_xref_cnt);
} _os_object_s;
#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \
isa; /* must be pointer-sized */ \
int volatile ref_cnt; \
int volatile xref_cnt
把宏展开,得到直观的定义:
typedef struct _os_object_s {
const _os_object_vtable_s *os_obj_isa; // 这个也是个宏定义,展开后似乎是可以被子类重写的和引用计数相关的两个函数指针
int volatile os_obj_ref_cnt; // 引用计数,这是内部gcd内部使用的计数器
int volatile os_obj_xref_cnt; // 外部引用计数,这是gcd外部使用的计数器,两者都为0的时候才能dispose
} _os_object_s;
struct dispatch_object_s {
_DISPATCH_OBJECT_HEADER(object);
};
#define _DISPATCH_OBJECT_HEADER(x) \
struct _os_object_s _as_os_obj[0]; \
OS_OBJECT_STRUCT_HEADER(dispatch_##x); \
struct dispatch_##x##_s *volatile do_next; \
struct dispatch_queue_s *do_targetq; \
void *do_ctxt; \
void *do_finalizer
宏展开:
struct dispatch_object_s {
struct _os_object_s _as_os_obj[0];
OS_OBJECT_STRUCT_HEADER(dispatch_object); // 继承自_os_object_s的部分,和引用计数相关
struct dispatch_object_s *volatile do_next; // 链表的 next
struct dispatch_queue_s *do_targetq; // 目标队列,指定这个object在哪个queue中执行
void *do_ctxt; // 上下文,我们要传递的参数
void *do_finalizer; // 析构函数
};
struct dispatch_queue_s {
_DISPATCH_QUEUE_HEADER(queue);
} DISPATCH_ATOMIC64_ALIGN;
#define _DISPATCH_QUEUE_HEADER(x) \
struct os_mpsc_queue_s _as_oq[0]; \
DISPATCH_OBJECT_HEADER(x); \
_OS_MPSC_QUEUE_FIELDS(dq, dq_state); \
uint32_t dq_side_suspend_cnt; \
dispatch_unfair_lock_s dq_sidelock; \
union { \
dispatch_queue_t dq_specific_q; \
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; \
}; \
DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, \
const uint16_t dq_width, \
const uint16_t __dq_opaque \
); \
宏展开:
struct dispatch_queue_s {
struct os_mpsc_queue_s _as_oq[0];
DISPATCH_OBJECT_HEADER(x);
_OS_MPSC_QUEUE_FIELDS(dq, dq_state); // 这里又是一个宏,需要再展开
uint32_t dq_side_suspend_cnt;
dispatch_unfair_lock_s dq_sidelock;
union {
dispatch_queue_t dq_specific_q;
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;
};
DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags,
const uint16_t dq_width,
const uint16_t __dq_opaque
);
}
#define _OS_MPSC_QUEUE_FIELDS(ns, __state_field__) \
struct dispatch_object_s *volatile ns##_items_head; \
DISPATCH_UNION_LE(uint64_t volatile __state_field__, \
dispatch_lock __state_field__##_lock, \
uint32_t __state_field__##_bits \
) DISPATCH_ATOMIC64_ALIGN; \
/* LP64 global queue cacheline boundary */ \
unsigned long ns##_serialnum; \
const char *ns##_label; \
struct dispatch_object_s *volatile ns##_items_tail; \
dispatch_priority_t ns##_priority; \
int volatile ns##_sref_cnt
展开_OS_MPSC_QUEUE_FIELDS
后:
struct dispatch_queue_s {
struct os_mpsc_queue_s _as_oq[0];
// 展开_OS_MPSC_QUEUE_FIELDS
struct dispatch_object_s *volatile queue_items_head; // queue首元素
DISPATCH_UNION_LE(
uint64_t volatile dq_state, // queue的状态
dispatch_lock dq_state_lock,
uint32_t dq_state_bits
) DISPATCH_ATOMIC64_ALIGN;
unsigned long queue_serialnum; // queue的编号
const char *queue_label; // queue的名称
struct dispatch_object_s *volatile queue_items_tail; // queue 尾元素
dispatch_priority_t queue_priority; // queue优先级
int volatile queue_sref_cnt
uint32_t dq_side_suspend_cnt;
dispatch_unfair_lock_s dq_sidelock;
union { // 这些是提交的queue上的元素队列吗????????
dispatch_queue_t dq_specific_q;
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;
};
DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags,
const uint16_t dq_width, // queue的并发数(dq_width==1表示是串行队列)
const uint16_t __dq_opaque
);
}
typedef struct dispatch_continuation_s {
struct dispatch_object_s _as_do[0];
DISPATCH_CONTINUATION_HEADER(continuation);
} *dispatch_continuation_t;
#define DISPATCH_CONTINUATION_HEADER(x) \
union { \
const void *do_vtable; \
uintptr_t dc_flags; \
}; \
union { \
pthread_priority_t dc_priority; \
int dc_cache_cnt; \
uintptr_t dc_pad; \
}; \
struct dispatch_##x##_s *volatile do_next; \
struct voucher_s *dc_voucher; \
dispatch_function_t dc_func; \
void *dc_ctxt; \
void *dc_data; \
void *dc_other
宏展开后
typedef struct dispatch_continuation_s {
struct dispatch_object_s _as_do[0];
union {
const void *do_vtable;
uintptr_t dc_flags;
};
union {
pthread_priority_t dc_priority;
int dc_cache_cnt;
uintptr_t dc_pad;
};
struct dispatch_continuation_s *volatile do_next;
struct voucher_s *dc_voucher;
dispatch_function_t dc_func;
void *dc_ctxt;
void *dc_data;
void *dc_other
} *dispatch_continuation_t;
struct dispatch_queue_attr_s {
OS_OBJECT_STRUCT_HEADER(dispatch_queue_attr);
dispatch_priority_requested_t dqa_qos_and_relpri; // queue优先级
uint16_t dqa_overcommit:2; // 是否可以overcommit
uint16_t dqa_autorelease_frequency:2;
uint16_t dqa_concurrent:1; // 是否是并发队列
uint16_t dqa_inactive:1; // 是否激活
};
我们要使用GCD,需要把任务提交到队列。而我们可以用如下三种方法之一获取要使用的队列:
dispatch_queue_t
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr)
dispatch_queue_t dispatch_get_main_queue(void)
dispatch_queue_t
dispatch_get_global_queue(long identifier, unsigned long flags)
第一种方法是创建一个queue,后两种方法是获取系统自定义的queue。
但其实背后的实现是,无论是自己创建还是获取系统定义的queue,只会在GCD启动时创建的root queue数组中,取得一个queue而已。
用户层面的queue和root queue之间的关系如下图所示:
TODO
root queue一共有12个,分别有不同的优先级和序列号。
让我们一起看看它们的实现,首先,是dispatch_queue_create
。
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
return _dispatch_queue_create_with_target(label, attr,
DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
可以看到,当用户要创建一个queue的时候,需要指定target queue,即创建的queue最终是在哪个queue上执行的,这称之为target queue
。对于用户创建的queue来说,这个target queue会取root queue之一。
这里的DISPATCH_TARGET_QUEUE_DEFAULT
是一个宏定义:
#define DISPATCH_TARGET_QUEUE_DEFAULT NULL
static dispatch_queue_t
_dispatch_queue_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
if (!slowpath(dqa)) { // 如果是串行队列
dqa = _dispatch_get_default_queue_attr(); // 串行队列的attr 取默认attr
} else if (dqa->do_vtable != DISPATCH_VTABLE(queue_attr)) { // 并行队列的 attr->do_vtable 应该等于 DISPATCH_VTABLE(queue_attr)
DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
}
//
// Step 1: Normalize arguments (qos, overcommit, tq)
//
// dispatch_qos_t qos是优先级?
dispatch_qos_t qos = _dispatch_priority_qos(dqa->dqa_qos_and_relpri);
// 是否overcommit(即queue创建的线程数是否允许超过实际的CPU个数)
_dispatch_queue_attr_overcommit_t overcommit = dqa->dqa_overcommit;
if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) { //
if (tq->do_targetq) { // overcommit 的queue 必须是全局的
DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
"a non-global target queue");
}
}
// 下面这些代码,因为用户创建的queue的tq一定为NULL,因此,只要关注tq == NULL的分支即可,我们删除了其余分支
if (!tq) { // 自己创建的queue,tq都是null
tq = _dispatch_get_root_queue( // 在root queue里面去取一个合适的queue当做target queue
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 无论是用户创建的串行还是并行队列,其qos都没有指定,因此,qos这里都取DISPATCH_QOS_DEFAULT
overcommit == _dispatch_queue_attr_overcommit_enabled);
if (slowpath(!tq)) { // 如果根据create queue是传入的属性无法获取到对应的tq,crash
DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
}
}
//
// Step 2: Initialize the queue
//
const void *vtable;
dispatch_queue_flags_t dqf = 0;
// 根据不同的queue类型,设置vtable。vtable实现了SERIAL queue 和 CONCURRENT queue的行为差异。
if (dqa->dqa_concurrent) { // 并发
vtable = DISPATCH_VTABLE(queue_concurrent);
} else { // 串行
vtable = DISPATCH_VTABLE(queue_serial);
}
// 创建一个与tq对应的dq,用来给用户返回
dispatch_queue_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_queue_s) - DISPATCH_QUEUE_CACHELINE_PAD);
// 初始化dq,可以看到dqa->dqa_concurrent,对于并发队列,其queue width是DISPATCH_QUEUE_WIDTH_MAX,而串行队列其width是1
_dispatch_queue_init(dq, dqf, dqa->dqa_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqa->dqa_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
dq->dq_label = label; // 设置dq的名字
#if HAVE_PTHREAD_WORKQUEUE_QOS
dq->dq_priority = dqa->dqa_qos_and_relpri;
if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
}
#endif
_dispatch_retain(tq);
if (qos == QOS_CLASS_UNSPECIFIED) { // 如果没有指定queue的优先级,则默认继承target queue的优先级
// legacy way of inherithing the QoS from the target
_dispatch_queue_priority_inherit_from_target(dq, tq);
}
if (!dqa->dqa_inactive) {
_dispatch_queue_inherit_wlh_from_target(dq, tq);
}
dq->do_targetq = tq; // 这一步,很关键!!! 将root queue设置为dq的target queue,root queue和新创建的queue联合在了一起
_dispatch_object_debug(dq, "%s", __func__);
// 将新创建的dq,添加到GCD内部管理的叫做_dispatch_introspection的queue列表中。这是GCD内部维护的一个queue列表,具体作用不太清楚。
return _dispatch_introspection_queue_create(dq);
}
去除多余的判断,其实逻辑也很简单,当我们调用dispatch_queue_create
,GCD内部会调用_dispatch_queue_create_with_target
, 它首先会根据我们创建的queue的属性:DISPATCH_QUEUE_SERIAL
或DISPATCH_QUEUE_CONCURRENT
,到root queue数组
中取出一个对应的queue作为target queue
。然后,会新建一个dispatch_queue_t对象,并设置其target queue,返回给用户。同时,在GCD内部,新建的queue还会被加入introspection queue列表
中。
这里的关键是根据queue属性获取对应的target root queue:
tq = _dispatch_get_root_queue( // 在root queue里面去取一个合适的queue当做target queue
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 串行 用DISPATCH_QOS_DEFAULT, 并行 用自己的qos
_dispatch_get_root_queue
的实现如下:
static inline dispatch_queue_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
return &_dispatch_root_queues[2 * (qos - 1) + overcommit]; // 根据qos和 是否overcommit,取得root queues数组中对应的queue(一共有12个root queue)
}
qos==DISPATCH_QOS_DEFAULT
,值是4。根据公式,2*(4-1) + 1 = 7, 所有创建的串行队列应该取root queue数组的第8个queue,而由于并行队列的overcommit是false(0),并行队列回去root queue的第7个元素:
// 用户创建的并行队列,默认会使用这个target queue
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE,
.dq_label = "com.apple.root.default-qos",
.dq_serialnum = 10,
),
// 用户创建的串行队列,会使这个target queue, main queue 也会取这个值,但是会将 .dq_label = "com.apple.main-thread",
// .dq_atomic_flags = DQF_THREAD_BOUND | DQF_CANNOT_TRYSYNC | DQF_WIDTH(1),
// .dq_serialnum = 1,
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.default-qos.overcommit",
.dq_serialnum = 11,
),
OK,现在我们已经知道用户自创建的queue默认都会附加到root queue上。那么对于dispatch_get_main_queue
, dispatch_get_global_queue
是否也有类似的逻辑呢?我们先来看dispatch_get_global_queue
。
我们通常会用如下代码,来获取一个全局的并发队列,并指定其优先级。
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
其实现如下:
dispatch_queue_t
dispatch_get_global_queue(long priority, unsigned long flags)
{
if (flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT) {
return DISPATCH_BAD_INPUT;
}
dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority); // 将用户使用的优先级转换为root queue的优先级
if (qos == DISPATCH_QOS_UNSPECIFIED) {
return DISPATCH_BAD_INPUT;
}
return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT); // 由于flags是保留值,均取0,因此global queue都是no overcommit的
}
逻辑很简单,首先将外部传入的queue优先级转换为GCD内部的优先级dispatch_qos_t qos
。 然后,在调用_dispatch_get_root_queue
获取root queue中对应的queue。
我们来看一下_dispatch_qos_from_queue_priority
的实现:
static inline dispatch_qos_t
_dispatch_qos_from_queue_priority(long priority)
{
switch (priority) {
case DISPATCH_QUEUE_PRIORITY_BACKGROUND: return DISPATCH_QOS_BACKGROUND;
case DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE: return DISPATCH_QOS_UTILITY;
case DISPATCH_QUEUE_PRIORITY_LOW: return DISPATCH_QOS_UTILITY;
case DISPATCH_QUEUE_PRIORITY_DEFAULT: return DISPATCH_QOS_DEFAULT;
case DISPATCH_QUEUE_PRIORITY_HIGH: return DISPATCH_QOS_USER_INITIATED;
default: return _dispatch_qos_from_qos_class((qos_class_t)priority);
}
}
/*!
* @function dispatch_get_main_queue
*
* @abstract
* Returns the default queue that is bound to the main thread.
*
* @discussion
* In order to invoke blocks submitted to the main queue, the application must
* call dispatch_main(), NSApplicationMain(), or use a CFRunLoop on the main
* thread.
*
* @result
* Returns the main queue. This queue is created automatically on behalf of
* the main thread before main() is called.
*/
dispatch_queue_t
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q);
}
根据注释,可以看到,main queue是由系统在main()方法调用前创建的。专门绑定到main thread上。而且,为了触发提交到main queue上的block,和其他queue不一样,main queue上的任务是依赖于main runloop触发的
。
struct dispatch_queue_s _dispatch_main_q = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
.do_targetq = &_dispatch_root_queues[
DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT], // 同样也是取root queue中的queue作为target queue
#endif
.dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
DISPATCH_QUEUE_ROLE_BASE_ANON,
.dq_label = "com.apple.main-thread",
.dq_atomic_flags = DQF_THREAD_BOUND | DQF_CANNOT_TRYSYNC | DQF_WIDTH(1),
.dq_serialnum = 1,
};
从上面源码可以看出,GCD用到的queue,无论是自己创建的,或是获取系统的main queue还是global queue,其最终都是落脚于GCD root queue中。 我们可以在代码中的任意位置创建queue,但最终GCD管理的,也不过这12个root queue,这种思路有点类似于命令模式
,即分散创建,集中管理
。