Dispatch Queue 是什么呢?
如如其名称所示,是执行处理的等待队列。应用程序编程人员通过 dispatch async
函数等 API ,在 Block
语法中记述想执行的处理并将其追加到 Dispatch Queue
中。Dispatch Queue
按照追加的顺序(先进先出 FIFO
, First-In-First-Out)执行处理。
Dispatch Queue 的特点
- 提供了一个简单明了的编程接口。
- 提供自动和整体的线程池管理。
- 内存效率要高得多(因为线程堆栈不会停留在应用程序内存中)。
- 将任务异步分派到 Dispatch Queue 不会使队列死锁。
Dispatch Queue 的种类
另外在执行处理时存在两种 Dispatch Queue ,一种是等待现在执行中处理的串行队列(Serial Dispatch Queue),另一种是不等待现在执行中处理的并发队列(Concurrent Dispatch Queue),如表所示。
Dispatch Queue 的种类 | 说明 |
---|---|
Serial Dispatch Queue | 等待现在执行中处理结束 |
Concurrent Dispatch Queue | 不等待现在执行中处理结束 |
Concurrent Dispatch Queue 虽然不用等待处理结束,可以并行执行多个处理,但并行执行的处理数最取决于当前系统的状态。即 iOS 和 OS X 基于 Dispatch Queue 中的处理数、CPU 核数以及 CPU 负荷等当前系统的状态来决定 Concurrent Dispatch Queue 中并行执行的处理数。所谓“并行执行。就是使用多个线程同时执行多个处理。
Dispatch Queue 的获取
虽然知道了有 Serial Dispatch Queue 和 Concurrent Dispatch Queue 这两种,但如何才能得到这些 Dispatch Queue 呢?方法有两种。
dispatch_queue_create
第一种方法是通过 GCD 的 API 生成 Dispatch Queue, dispatch queue create
函数可生成 Dispatch Queue。
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr);
该函数的第一个参数指定 Serial Dispatch Queue 的名称。 Dispatch Queue 的名称推荐使用应用程序 ID 这种逆序全程域名 (FQDN, fully qualified domain name) 。该名称在 Xcode 和 Instruments 的调试器中作为 Dispatch Queue 名称表示。另外,该名称也出现在应用程序崩溃时所生成的 CrashLog 中。我们命名时应遵循这样的原则:对我们编程人员来说简单易懂,对用户来说也要易憧。
生成 Serial Dispatch Queue.像下面代码这样,将第二个参数指定为 NULL
。生成 *Concurrent Dispatch Queue 时,像下面代码一样,指定为 DISPATCH_QUEUE_CONCURRENT
。
-
Serial Dispatch Queue
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create ("com.example.gcd.MySerialDispatchQueue",NULL);
如前所述,Concurrent Dispatch Queue 并行执行多个追加处理,而 Serial Dispatch Queue 同时只能执行 1 个追加处理。虽然 Serial Dispatch Queue 和 Concurrent Dispatch Queue 受到系统资源的限制,但用
dispatch queue create
函数可生成任意多个 Dispatch Queue 。
当生成多个 Serial Dispatch Queue 时,各个 Serial Dispatch Queue 将并行执行。虽然在 1 个 Serial Dispatch Queue 中同时只能执行一个迫加处理,但如果将处理分别追加到 4 个 Serial Dispatch Queue 中,各个 Serial Dispatch Queue 执行 1 个,即为同时执行 4 个处理。
像之前列举的多线程编程问题一样,如果过多使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。
-
Concurrent Dispatch Queue
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.gcd.MyConcurrentDispatchQueue",DISPATCH_QUEUE_CONCURRENT);
Main Dispatch Queue/Global Dispatch Queue
除了 dispatch_queue_create
创建,还可以获取系统标准提供的 Dispatch Queue 。实际上不用特意生成 Dispatch Queue 。系统也会给我们提供几个。那就是 Main Dispatch Queue 和 Global Dispalch Queue 。
-
Main Dispatch Queue
Main Dispatch Queue 正如其名称中含有的 Main 一样,是在主线程中执行的 Dispatch Queue。因为主线程只冇 1 个,所以 Main Dispatch Queue 自然就是 Serial Dispatch Queue。
追加到 Main Dispatch Queue 的处理在主线程的RunLoop
中执行。由于在主线程中执行,因此要将用户界面的更新等一些必须在主线程中执行的处理追加到 Main Dispatch Queue 使用。 这正好与NSObject
类的performSelectorOnMainThread
实例方法这一执行方法相同。
/** Main Dispatch Queue 的获取方法 */ dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
-
Global Dispatch Queue
另一个 Global Dispatch Queue 是所有应用程序都能够使用的 Concurrent Dispatch Queue 。虽然 Global Dispatch Queue 使用起来较为方便,但是在应用程序开发中,使用dispatch_queue_create
创建维护 Dispatch_Queue 是有必要的。/** Global Dispatch Queue (默认优先级)的获取方法 */ dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAUL,0);
-
优先级
另外,Global Dispatch Queue 有 4 个执行优先级,分别是高优先级( High Priority ),默认优先级( Default Priority )、低优先级( Low Priority )和后台优先级( Background Priority )。通过 XNU 内核管理的用于 Global Dispatch Queue 的线程,将各自使用的 Global Dispatch Queue 的执行优先级作为线程的执行优先级使用。在向 Global Dispatch Queue 追加处理时,应选择与处理内容对应的执行优先级的 Global Dispatch Queue 。名称 Dispatch Queue 的种类 说明 Main Dispatch Queue Serial Dispatch Queue 主线程执行 Global Dispatch Queue ( High Priority ) Concurrent Dispatch queue 执行优先級:高(最高优先) Global Dispatch Queue ( Default Priority ) Concurrent Dispatch queue 执行优先級:默认 Global Dispatch Queue ( Low Priority ) Concurrent Dispatch queue 执行优先级:低 Global Dispatch Queue ( Background Priority ) Concurrent Dispatch queue 执行优先级:后台
dispatch_get_main_queue 实现
dispatch_get_main_queue 源码
打开 libdispatch 工程后搜索 dispatch_get_main_queue(void)
,找到源码如下:
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
DISPATCH_GLOBAL_OBJECT
DISPATCH_GLOBAL_OBJECT
是一个宏,因此等于 __bridge dispatch_queue_main_t &_dispatch_main_q
;
#define OS_OBJECT_BRIDGE __bridge
#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))
_dispatch_main_q
搜索 _dispatch_main_q
为 dispatch_queue_static_s
结构体,可以看到主队列在定义时就赋值,源码如下:
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_introspection_init
在工程中搜索 _dispatch_main_q
,可以看到 dispatch_introspection_init
函数中调用 _dispatch_trace_queue_create(&_dispatch_main_q);
; 而 dispatch_introspection_init
在 libdispatch_init
就被调用。因此,主队列的创建是在 libdispatch.dylib 初始化时。
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_queue_create 实现
打开 libdispatch 工程后搜索 dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
关键字,源码如下:
#define DISPATCH_TARGET_QUEUE_DEFAULT NULL
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);
}
通过 dispatch_queue_create
函数内部实现可以得知,调用 _dispatch_lane_create_with_target
私有函数,并传递标签 label
与类型 attr
参数。 其中 DISPATCH_TARGET_QUEUE_DEFAULT
是宏定义 NULL
。
dispatch_queue_attr_t attr 是什么
dispatch_queue_attr_t
类型是 dispatch queues
的属性,一般地,在调用 dispatch_queue_create
函数时,会赋值 DISPATCH_QUEUE_SERIAL
和 DISPATCH_QUEUE_CONCURRENT
,其宏定义源码如下:
#define DISPATCH_QUEUE_SERIAL NULL
#define DISPATCH_QUEUE_CONCURRENT \
DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, \
_dispatch_queue_attr_concurrent)
DISPATCH_QUEUE_CONCURRENT
可以看到 DISPATCH_QUEUE_CONCURRENT
其实是 _dispatch_queue_attr_concurrent
#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))
struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_attr),
};
DISPATCH_GLOBAL_OBJECT_HEADER
DISPATCH_GLOBAL_OBJECT_HEADER
是一个宏代码执行,源码如下:
#define DISPATCH_GLOBAL_OBJECT_HEADER(name) \
.do_vtable = DISPATCH_VTABLE(name), \
.do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
.do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
#endif
DISPATCH_VTABLE
通过 DISPATCH_VTABLE
拼接队列类型,赋值 vtable
变量 ,其中 DISPATCH_VTABLE()
是宏定义,最终实现是 OS_dispatch_##name##_class
#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
#define DISPATCH_OBJC_CLASS(name) (&DISPATCH_CLASS_SYMBOL(name))
#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class
_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)
//
dispatch_qos_t qos = dqai.dqai_qos;
#if !HAVE_PTHREAD_WORKQUEUE_QOS
if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
}
if (qos == DISPATCH_QOS_MAINTENANCE) {
dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
}
#endif // !HAVE_PTHREAD_WORKQUEUE_QOS
_dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
//代码过长省略部分判断
if (!tq) {
uintptr_t flags = (overcommit == _dispatch_queue_attr_overcommit_enabled) ? DISPATCH_QUEUE_OVERCOMMIT : 0;
//tq初始化
tq = _dispatch_get_root_queue(
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,
flags)->_as_dq;
if (unlikely(!tq)) {
DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
}
}
//
// Step 2: Initialize the queue
//
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) {
OS_dispatch_name
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
vtable = DISPATCH_VTABLE(queue_serial);
}
switch (dqai.dqai_autorelease_frequency) {
case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
dqf |= DQF_AUTORELEASE_NEVER;
break;
case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
dqf |= DQF_AUTORELEASE_ALWAYS;
break;
}
if (label) {
const char *tmp = _dispatch_strdup_if_mutable(label);
if (tmp != label) {
dqf |= DQF_LABEL_NEEDS_FREE;
label = tmp;
}
}
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;
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;
}
查看_dispatch_lane_create_with_target
函数实现,执行流程如下:
-
_dispatch_queue_attr_to_info
函数 创建dispatch_queue_attr_info_t
类型的dqai
,其作用就是保存 Dispatch Queue 的属性信息:dqai_concurrent
等等,源码如下:```cpp 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_info_t _dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa) { dispatch_queue_attr_info_t dqai = { }; if (!dqa) return dqai; #if DISPATCH_VARIANT_STATIC if (dqa == &_dispatch_queue_attr_concurrent) { dqai.dqai_concurrent = true; return dqai; } #endif if (dqa < _dispatch_queue_attrs || dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) { #ifndef __APPLE__ if (memcmp(dqa, &_dispatch_queue_attrs[0], sizeof(struct dispatch_queue_attr_s)) == 0) { dqa = (dispatch_queue_attr_t)&_dispatch_queue_attrs[0]; } else #endif // __APPLE__ DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute"); } size_t idx = (size_t)(dqa - _dispatch_queue_attrs); //...代码过长省略 return dqai; } ```
-
规范化参数,例如 qos, overcommit, tq
由于在
dispatch_queue_create
函数中调用_dispatch_lane_create_with_target
时,tq 参数 为DISPATCH_TARGET_QUEUE_DEFAULT
,在源码中找到代码#define DISPATCH_TARGET_QUEUE_DEFAULT NULL
,故 tq 不存在,经过_dispatch_get_root_queue
初始化后获取_dispatch_root_queues
中的队列。static inline dispatch_queue_global_t _dispatch_get_root_queue(dispatch_qos_t qos, uintptr_t flags) { if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) { DISPATCH_CLIENT_CRASH(qos, "Corrupted priority"); } unsigned int add_on = 0; if (flags & DISPATCH_QUEUE_OVERCOMMIT) { add_on = DISPATCH_ROOT_QUEUE_IDX_OFFSET_OVERCOMMIT; } else if (flags & DISPATCH_QUEUE_COOPERATIVE) { add_on = DISPATCH_ROOT_QUEUE_IDX_OFFSET_COOPERATIVE; } return &_dispatch_root_queues[3 * (qos - 1) + add_on]; }
根据
dqai_concurrent
,通过DISPATCH_VTABLE
生成vtable
,串行队列:OS_dispatch_queue_serial,并发队列: OS_dispatch_queue_concurrent;-
通过
_dispatch_object_alloc
创建dq
,_os_object_alloc_realized
根据vtable
的值 与size
创建_os_obj
,设置了 isa 指向, 可知dq
为 对象。void * _dispatch_object_alloc(const void *vtable, size_t size) { #if OS_OBJECT_HAVE_OBJC1 const struct dispatch_object_vtable_s *_vtable = vtable; dispatch_object_t dou; dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size); dou._do->do_vtable = vtable; return dou._do; #else return _os_object_alloc_realized(vtable, size); #endif } inline _os_object_t _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; }
-
通过
_dispatch_queue_init
初始化dq
,根据 Dispatch Queue 类型是否并发,生成的width
不同,也就是可执行任务数量不同,串行等于 1 ,并发等于DISPATCH_QUEUE_WIDTH_MAX
也就是(DISPATCH_QUEUE_WIDTH_FULL - 2)
,DISPATCH_QUEUE_WIDTH_FULL
值等于0x1000ull
, 最终DISPATCH_QUEUE_WIDTH_MAX
等于 。```cpp static inline dispatch_queue_class_t _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->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; 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; } ```
通过
_dispatch_trace_queue_create
对创建的队列进行处理,其中_dispatch_trace_queue_create
是_dispatch_introspection_queue_create
封装的宏定义,最后会返回处理过的_dq
#define _dispatch_trace_queue_create _dispatch_introspection_queue_create
_dispatch_introspection_queue_create 实现
_dispatch_introspection_queue_create(dispatch_queue_t dq)
{
dispatch_queue_introspection_context_t dqic;
size_t sz = sizeof(struct dispatch_queue_introspection_context_s);
if (!_dispatch_introspection.debug_queue_inversions) {
sz = offsetof(struct dispatch_queue_introspection_context_s,
__dqic_no_queue_inversion);
}
dqic = _dispatch_calloc(1, sz);
dqic->dqic_queue._dq = dq;
if (_dispatch_introspection.debug_queue_inversions) {
LIST_INIT(&dqic->dqic_order_top_head);
LIST_INIT(&dqic->dqic_order_bottom_head);
}
dq->do_introspection_ctxt = dqic;
_dispatch_unfair_lock_lock(&_dispatch_introspection.queues_lock);
LIST_INSERT_HEAD(&_dispatch_introspection.queues, dqic, dqic_list);
_dispatch_unfair_lock_unlock(&_dispatch_introspection.queues_lock);
DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create, dq);
if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create)) {
_dispatch_introspection_queue_create_hook(dq);
}
return upcast(dq)._dqu;
}
_dispatch_introspection_queue_create
函数执行流程如下:
创建
dispatch_queue_introspection_context_t
类型的dqic
并与dq
进行关联-
调用
_dispatch_introspection_queue_create_hook
函数static void _dispatch_introspection_queue_create_hook(dispatch_queue_t dq) { dispatch_introspection_queue_s diq; diq = dispatch_introspection_queue_get_info(dq); dispatch_introspection_hook_callout_queue_create(&diq); }
-
调用
dispatch_introspection_queue_get_info
dispatch_introspection_queue_s dispatch_introspection_queue_get_info(dispatch_queue_t dq) { if (dx_metatype(dq) == _DISPATCH_WORKLOOP_TYPE) { return _dispatch_introspection_workloop_get_info(upcast(dq)._dwl); } return _dispatch_introspection_lane_get_info(upcast(dq)._dl); }
-
调用
_dispatch_introspection_lane_get_info
static inline dispatch_introspection_queue_s _dispatch_introspection_lane_get_info(dispatch_lane_class_t dqu) { dispatch_lane_t dq = dqu._dl; bool global = _dispatch_object_is_global(dq); uint64_t dq_state = os_atomic_load2o(dq, dq_state, relaxed); dispatch_introspection_queue_s diq = { .queue = dq->_as_dq, .target_queue = dq->do_targetq, .label = dq->dq_label, .serialnum = dq->dq_serialnum, .width = dq->dq_width, .suspend_count = _dq_state_suspend_cnt(dq_state) + dq->dq_side_suspend_cnt, .enqueued = _dq_state_is_enqueued(dq_state) && !global, .barrier = _dq_state_is_in_barrier(dq_state) && !global, .draining = (dq->dq_items_head == (void*)~0ul) || (!dq->dq_items_head && dq->dq_items_tail), .global = global, .main = dx_type(dq) == DISPATCH_QUEUE_MAIN_TYPE, }; return diq; }
总结
通过上面的函数调用流程,可知 Dispatch Queue 最终是通过结构体模板创建的。