通过前两章的学习,咱们对多线程和GCD的使用已经有了了解,可是对于底层原理并不熟悉,咱们知道GCD使用主要有队列和函数两个参数,咱们就来一一探究,本章节咱们先来看一下GCD的队列是如何建立的git
系列文章传送门:github
☞ iOS底层学习 - 多线程之GCD初探swift
咱们已经知道了,GCD中的队列(FIFO
)主要有如下四种:数组
咱们能够经过dispatch_queue_create
方法来生成一个dispatch_queue_t
对象供GCD
来使用,那么在底层队列是如何建立的呢?markdown
dispatch_queue_create
要像知道是底层是如何建立的,最好的方法仍是阅读源码,万幸的是,多线程的代码,苹果是开源的,能够点击libdispatch源码下载。多线程
在底层源码中,咱们能够看到dispatch_queue_create
的底层实现以下,能够看到调用了一个中间代码方法。下面咱们深刻_dispatch_lane_create_with_target
方法,来看一下究竟。并发
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) { return _dispatch_lane_create_with_target(label, attr, DISPATCH_TARGET_QUEUE_DEFAULT, true); } 复制代码
因为此方法比较长,且调用的相关方法比较多,咱们会截取一部分重点来说。app
dispatch_queue_attr_info_t
其中参数dispatch_queue_attr_t
为传入的串行仍是并行队列参数,咱们知道串行队列传入的是NULL
。根据代码能够发现,将dqa
参数传入后,调用了_dispatch_queue_attr_to_info
方法,生成了dispatch_queue_attr_info_t
对象dqai
。ide
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); ... } 复制代码
咱们能够发现dispatch_queue_attr_info_t
是一个结构体位域的形式,里面包含了一些dispatch_qos_t
等优先级的值。
typedef struct dispatch_queue_attr_info_s { dispatch_qos_t dqai_qos : 8; int dqai_relpri : 8; uint16_t dqai_overcommit:2; uint16_t dqai_autorelease_frequency:2; uint16_t dqai_concurrent:1; uint16_t dqai_inactive:1; } dispatch_queue_attr_info_t; 复制代码
而查看_dispatch_queue_attr_to_info
方法咱们能够知道,当入参为NULL
的时候,是直接返回一个空结构体的,也就是说当串行队列时,返回的是空值,因此代码后续对dqai
的操做都是基于并发队列的,并经过此来进行判断取值。
而并发队列,会根据传入的宏定义参数,经过位运算,来给dqai
进行赋值,比较主要的有并发数(dqai_concurrent
),优先级(dqai_qos
)等
dispatch_queue_attr_info_t _dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa) { dispatch_queue_attr_info_t dqai = { }; if (!dqa) return dqai; .... // 苹果的算法 size_t idx = (size_t)(dqa - _dispatch_queue_attrs); // 位域 // 0000 000000000 00000000000 0000 000 1 dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT); idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT; dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT); idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT; dqai.dqai_relpri = -(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT); idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT; dqai.dqai_qos = idx % DISPATCH_QUEUE_ATTR_QOS_COUNT; idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT; dqai.dqai_autorelease_frequency = idx % DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT; idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT; dqai.dqai_overcommit = idx % DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT; idx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT; ... } 复制代码
dispatch_lane_t
咱们虽然获取到了dispatch_queue_attr_info_t
相关的值,可是它并非最终返回的线程,只是咱们用来获取一些配置的临时变量而已。经过看代码发现最终返回的是一个叫dq
的dispatch_lane_t
对象,因此dispatch_lane_t
应该是最终生成的队列,咱们能够发现它是由_dispatch_object_alloc
方法建立出来的。
可是_dispatch_object_alloc
方法并无开源,因此咱们不得而知。不过_dispatch_object
很像OC中的NSObject
,是否是它也是一个抽象相似得存在呢。
dispatch_lane_t dq = _dispatch_object_alloc(vtable, sizeof(struct dispatch_lane_s)); 复制代码
dispatch_object_t
(重点理解)经过搜索dispatch_object
,发现并找不到咱们须要的多线程抽象类,不过咱们发现,通常多线程的对象后面都有_t
,因此咱们找到了dispatch_object_t
这个多线程的抽象类。
咱们能够发现其是一个联合体,和咱们isa
的结果极其相似,里面包含了咱们经常使用的不少信息。由于联合体互斥,这样作有利于内存的优化,和更好的实现多态。
dispatch_object_s
结构体指针dispatch_queue_s
队列dispatch_queue_attr_s
参数值dispatch_group_s
组dispatch_semaphore_s
信号量typedef union { struct _os_object_s *_os_obj; struct dispatch_object_s *_do; 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_semaphore_s *_dsema; struct dispatch_data_s *_ddata; struct dispatch_io_s *_dchannel; } dispatch_object_t DISPATCH_TRANSPARENT_UNION; 复制代码
_dispatch_queue_init
当咱们建立出dispatch_lane_t
对象dq
后,紧接着就执行_dispatch_queue_init
构造方法。
_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_queue_t dq = dqu._dq;
取出队列后,对队列又进行了一系列的赋值,而后又返回了
_dispatch_queue_init(dispatch_queue_class_t dqu, dispatch_queue_flags_t dqf, uint16_t width, uint64_t initial_state_bits) { uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width); dispatch_queue_t dq = dqu._dq; dispatch_assert((initial_state_bits & ~(DISPATCH_QUEUE_ROLE_MASK | DISPATCH_QUEUE_INACTIVE)) == 0); if (initial_state_bits & DISPATCH_QUEUE_INACTIVE) { dq_state |= DISPATCH_QUEUE_INACTIVE + DISPATCH_QUEUE_NEEDS_ACTIVATION; dq->do_ref_cnt += 2; // rdar://8181908 see _dispatch_lane_resume if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) { dq->do_ref_cnt++; // released when DSF_DELETED is set } } dq_state |= (initial_state_bits & DISPATCH_QUEUE_ROLE_MASK); dq->do_next = DISPATCH_OBJECT_LISTLESS; dqf |= DQF_WIDTH(width); os_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed); dq->dq_state = dq_state; dq->dq_serialnum = os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed); return dqu; } 复制代码
dqai.dqai_concurrent ?DISPATCH_QUEUE_WIDTH_MAX : 1
咱们发如今构造时有一个三目运算符,判断了dqai.dqai_concurrent
,咱们知道串行是没有dqai
的,因此此时为1,就表示串行队列的并发数为1。
查看DISPATCH_QUEUE_WIDTH_MAX
宏定义,咱们发现为DISPATCH_QUEUE_WIDTH_FULL-2
,即0xFFE
,因此并发队列的最大并发数为0xFFE
。至于-2
则是由于-1
是为了避免饱和,在-1
是由于DISPATCH_QUEUE_WIDTH_POOL
为建立全局队列时候所使用的,避免相同
dq->do_targetq = tq;
执行完构造函数以后,接着又对dq
进行了一些列赋值。可是若是每次建立线程,全部的属性都要从新赋值的话,是比较耗性能的,因此队列的建立是基于"模板"的,这个"模板"就是咱们的do_targetq
属性。
...... // 标签 dq->dq_label = label; // 优先级 dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos, dqai.dqai_relpri); if (overcommit == _dispatch_queue_attr_overcommit_enabled) { dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT; } if (!dqai.dqai_inactive) { _dispatch_queue_priority_inherit_from_target(dq, tq); _dispatch_lane_inherit_wlh_from_target(dq, tq); } _dispatch_retain(tq); dq->do_targetq = tq; _dispatch_object_debug(dq, "%s", __func__); return _dispatch_trace_queue_create(dq)._dq; 复制代码
在方法中寻找,咱们能够发现tq
的建立是在根队列的基础上,获取到了优先级qos
和overcommit
的赋值。
首先看qos
的值,咱们发现DISPATCH_QOS_UNSPECIFIED
为0,且以前咱们并无赋值,因此通常状况下即执行DISPATCH_QOS_DEFAULT
,为4,因此qos
没指定的状况下为4。
接着看overcommit
的值,根据上面dqai
能够判断出,串行为1,并发为0
if (!tq) { tq = _dispatch_get_root_queue( qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4 overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1 if (unlikely(!tq)) { DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute"); } } 复制代码
接着咱们能够看_dispatch_get_root_queue
函数,经过代码咱们能够发现,执行的就是从根队列数组里经过下标来取出队列的逻辑,根据入参能够知道,下标为6或者7
static inline dispatch_queue_global_t _dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit) { if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) { DISPATCH_CLIENT_CRASH(qos, "Corrupted priority"); } // 4-1= 3 // 2*3+0/1 = 6/7 return &_dispatch_root_queues[2 * (qos - 1) + overcommit]; } 复制代码
查看_dispatch_root_queues[]
的定义可得以下代码。经过下标,咱们能够得出以下结论。
target
队列为com.apple.root.default-qos
target
队列为com.apple.root.default-qos.overcommit
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, ), _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, ), }; 复制代码
至此,咱们自定义的一个串行或者并发队列就已经根据模板建立完成了
经过上面的分析,咱们已经知道了自定义队列是根据根队列的模板来进行建立的。那么根队列又是什么时候建立的呢?
根据咱们以前的分析iOS底层学习 - 从编译到启动的奇幻旅程(二),在dyld
连接动态库时,会连接libdispatch
库,运行libdispatcdispatch_queue_createh_init
方法,找到了_dispatch_introspection_init
方法,该方法就是建立根队列的主要方法。
咱们能够看到该方法的主要实现就是一个for
循环执行_dispatch_trace_queue_create
方法,将上面_dispatch_root_queues[]
数组中的队列进行一一建立。
而且经过_dispatch_trace_queue_create(&_dispatch_main_q);
方法,建立了主队列,_dispatch_main_q
即表明主队列。
void _dispatch_introspection_init(void) { _dispatch_introspection.debug_queue_inversions = _dispatch_getenv_bool("LIBDISPATCH_DEBUG_QUEUE_INVERSIONS", false); // Hack to determine queue TSD offset from start of pthread structure uintptr_t thread = _dispatch_thread_self(); thread_identifier_info_data_t tiid; mach_msg_type_number_t cnt = THREAD_IDENTIFIER_INFO_COUNT; kern_return_t kr = thread_info(pthread_mach_thread_np((void*)thread), THREAD_IDENTIFIER_INFO, (thread_info_t)&tiid, &cnt); if (!dispatch_assume_zero(kr)) { _dispatch_introspection.thread_queue_offset = (void*)(uintptr_t)tiid.dispatch_qaddr - (void*)thread; } _dispatch_thread_key_create(&dispatch_introspection_key, _dispatch_introspection_thread_remove); _dispatch_introspection_thread_add(); // add main thread for (size_t i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { _dispatch_trace_queue_create(&_dispatch_root_queues[i]); } #if DISPATCH_USE_MGR_THREAD && DISPATCH_USE_PTHREAD_ROOT_QUEUES _dispatch_trace_queue_create(_dispatch_mgr_q.do_targetq); #endif _dispatch_trace_queue_create(&_dispatch_main_q); _dispatch_trace_queue_create(&_dispatch_mgr_q); } 复制代码
那么咱们为何能在任意地方经过dispatch_get_main_queue()
取到主队列呢?
经过打印主队列,获得如下结果过,并能够获得其名称
<OS_dispatch_queue_main: com.apple.main-thread>
经过搜索名称,咱们找到了主队列的定义。发现其为全局的静态变量,在里面有它的赋值
struct dispatch_queue_static_s _dispatch_main_q = { DISPATCH_GLOBAL_OBJECT_HEADER(queue_main), #if !DISPATCH_USE_RESOLVERS .do_targetq = _dispatch_get_default_queue(true), #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_WIDTH(1), .dq_serialnum = 1, }; 复制代码
那么咱们的根队列建立后,并发数是多少呢,刚才咱们知道了自定义队列的并发数为DISPATCH_QUEUE_WIDTH_MAX
,即(DISPATCH_QUEUE_WIDTH_FULL - 2)
。其中一个-1
的预留就是为了给根队列,即#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
。咱们搜索能够发如今根队列的数组中,规定了此并发数
dyld
连接时,根据宏定义的数组建立dyld
连接时,是一个全局的静态串行队列变量,因此可以随时取用