李峰峰博客

GCD 底层原理 2 - dispatch_queue

2022-10-25

一、概述

Grand Central Dispatch(GCD)是 iOS 中一个强大的多线程编程框架,GCD 提供了一种高效、低级别的方式来管理并发任务。

GCD 是使用队列来调度任务的执行的,GCD 队列主要有这几种:

  • 队列(Queue)
    • 串行队列:任务按顺序执行,一个任务完成后才开始下一个任务。
    • 并发队列:允许多个任务同时执行,任务的开始顺序是确定的,但完成顺序不一定。
  • 主队列(Main Queue)
    • 一个特殊的串行队列,用于在主线程上执行任务,通常用于更新 UI。
    • 所有提交到主队列的任务,无论使用 dispatch_sync 还是 dispatch_async,都将在主线程执行。
  • 全局并发队列(Global Concurrent Queue)
    • 系统提供的并发队列,按不同优先级划分。

GCD 任务是指需要执行的代码块,有同步或异步执行两种方式:

  • 同步执行(dispatch_sync):阻塞当前线程,直到任务完成。
  • 异步执行(dispatch_async):立即返回,不阻塞当前线程,任务在后台执行。

其中,创建队列是通过 GCD 的 dispatch_queue_create 进行创建的:

1
2
3
4
5
// 创建一个串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.lixkit.serialQueue", DISPATCH_QUEUE_SERIAL);

// 创建一个并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.lixkit.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

接下来,通过 libdispatch 源码,看 dispatch_queue_create 实现原理。

二、DISPATCH_QUEUE_SERIAL & DISPATCH_QUEUE_CONCURRENT

前面已经提到,创建队列是通过 dispatch_queue_create 进行创建的:

1
2
3
4
5
// 创建一个串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.lixkit.serialQueue", DISPATCH_QUEUE_SERIAL);

// 创建一个并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.lixkit.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

创建串行队列和并发队列,用到了这两个宏:

  • DISPATCH_QUEUE_SERIAL
  • DISPATCH_QUEUE_CONCURRENT

1、DISPATCH_QUEUE_SERIAL

DISPATCH_QUEUE_SERIAL 用于创建串行队列,这是个宏,其本质是 NULL

1
#define DISPATCH_QUEUE_SERIAL NULL

2、DISPATCH_QUEUE_CONCURRENT

DISPATCH_QUEUE_CONCURRENT 也是一个宏,且其定义中使用了一系列的宏定义:

1
2
3
4
5
6
7
8
9
#define DISPATCH_QUEUE_CONCURRENT \
DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, \
_dispatch_queue_attr_concurrent)

#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))

struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent;

#define OS_OBJECT_BRIDGE __bridge

将其完全展开后定义如下:

1
2
#define DISPATCH_QUEUE_CONCURRENT \
((__bridge dispatch_queue_attr_t)&(_dispatch_queue_attr_concurrent))

DISPATCH_QUEUE_CONCURRENTdispatch_queue_attr_t 类型的指针,该指针是由全局变量 _dispatch_queue_attr_concurrent 的地址强制转换成的。

_dispatch_queue_attr_concurrent 是什么呢?
上面提到,DISPATCH_QUEUE_CONCURRENT 是由全局变量 _dispatch_queue_attr_concurrent 转换成的,那么 _dispatch_queue_attr_concurrent 具体是什么呢?

可以看下 init.c 中下述源码:

1
2
3
4
5
6
7
// DISPATCH_QUEUE_CONCURRENT resp. _dispatch_queue_attr_concurrent is aliased
// to array member [0] and their properties must match!
const struct dispatch_queue_attr_s _dispatch_queue_attrs[] = {
[0 ... DISPATCH_QUEUE_ATTR_COUNT - 1] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_attr),
},
};

在注释中明确提到, DISPATCH_QUEUE_CONCURRENT_dispatch_queue_attr_concurrent 都是 _dispatch_queue_attrs 数组第 0 个元素的别名。即:DISPATCH_QUEUE_CONCURRENT 本质是 _dispatch_queue_attrs 数组第 0 个元素。

在上述 _dispatch_queue_attrs 数组的定义中,DISPATCH_QUEUE_ATTR_COUNT 宏定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define DISPATCH_QOS_USER_INTERACTIVE   ((dispatch_qos_t)6)
#define DISPATCH_QOS_MAX DISPATCH_QOS_USER_INTERACTIVE

#define QOS_MIN_RELATIVE_PRIORITY (-15)

#define DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT 3

#define DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT 3

#define DISPATCH_QUEUE_ATTR_QOS_COUNT (DISPATCH_QOS_MAX + 1)

#define DISPATCH_QUEUE_ATTR_PRIO_COUNT (1 - QOS_MIN_RELATIVE_PRIORITY)

#define DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT 2

#define DISPATCH_QUEUE_ATTR_INACTIVE_COUNT 2

#define DISPATCH_QUEUE_ATTR_COUNT ( \
DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT * \
DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT * \
DISPATCH_QUEUE_ATTR_QOS_COUNT * \
DISPATCH_QUEUE_ATTR_PRIO_COUNT * \
DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT * \
DISPATCH_QUEUE_ATTR_INACTIVE_COUNT )

可以看到 DISPATCH_QUEUE_ATTR_COUNT 是一系列宏参与乘法计算,其最终计算结果是 4032

DISPATCH_GLOBAL_OBJECT_HEADER 宏定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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

#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

#define DISPATCH_OBJECT_GLOBAL_REFCNT _OS_OBJECT_GLOBAL_REFCNT

#define _OS_OBJECT_GLOBAL_REFCNT INT_MAX

DISPATCH_GLOBAL_OBJECT_HEADER(queue_attr) 完全展开后如下:

1
2
3
.do_vtable = (&_dispatch_queue_attr_vtable), // 通过宏展开得出虚函数表指针
.do_ref_cnt = INT_MAX, // 全局对象的引用计数
.do_xref_cnt = INT_MAX // 全局对象的弱引用计数

综上, _dispatch_queue_attrs 实际定义为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// DISPATCH_QUEUE_CONCURRENT 和 _dispatch_queue_attr_concurrent 本质是 _dispatch_queue_attrs 数组第 0 个元素
const struct dispatch_queue_attr_s _dispatch_queue_attrs[] = {
// 使用 GNU C 的范围设计符号初始化数组
// [0 ... 4032 - 1] 指定数组的每个元素(从索引 0 到 4031,共计 4032 个元素)都被初始化为相同的值
[0 ... 4032 - 1] = {
// 初始化 do_vtable 成员为指向 _dispatch_queue_attr_vtable 的指针
// 这个指针用于指向虚函数表,定义了该结构体的行为和方法
.do_vtable = (&_dispatch_queue_attr_vtable), // 指向虚函数表的指针

// 初始化 do_ref_cnt 成员为 INT_MAX
// 这表示全局对象的引用计数,通常设置为 INT_MAX 以防止对象被释放
.do_ref_cnt = INT_MAX, // 全局对象的引用计数

// 初始化 do_xref_cnt 成员为 INT_MAX
// 这表示全局对象的弱引用计数,同样设置为 INT_MAX,以确保对象在程序运行期间始终有效
.do_xref_cnt = INT_MAX // 全局对象的弱引用计数
},
};

尽管 _dispatch_queue_attrs4032 个元素的初始值相同,但实际使用过程中会根据不同的条件或配置修改特定元素的属性,以支持不同的队列行为或策略。通过 _dispatch_queue_attrs 数组索引直接访问属性组合,而不是在运行时计算或生成新的组合,提升了访问的速度和效率。而 DISPATCH_QUEUE_CONCURRENT 则是 _dispatch_queue_attrs 数组的第 0 个元素。

三、dispatch_queue_create

dispatch_queue_create 函数源码如下:

1
2
3
4
5
6
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);
}

1、参数 label & attr

(1)label 参数

char * 类型的参数,用于为队列指定一个标识符,通常是一个描述性的字符串。label 参数对队列的功能和行为没有影响,主要用于调试和日志记录,帮助开发者识别和跟踪不同的队列。经常使用反向域名格式(如 "com.lixkit.serialQueue"),以确保标签的唯一性和可读性。

(2)attr 参数

attr 参数,直接从方法定义上看是 dispatch_queue_attr_t 类型,实际上,其源码实现如下:

1
2
DISPATCH_DECL(dispatch_queue_attr);
#define DISPATCH_DECL(name) typedef struct name##_s *name##_t

这意味着 dispatch_queue_attr_tstruct dispatch_queue_attr_s 的指针类型,dispatch_queue_attr_s 的定义涉及到一系列宏定义和其他结构体的嵌套,将其完全展开后源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct dispatch_queue_attr_s {
/**
对象系统的基础类型
_as_os_obj 是一个零长度数组,用于类型转换和内存布局
在对象系统中,这个字段用于支持对象模型的基础结构
*/
struct _os_object_s _as_os_obj[0];

/**
do_vtable 是一个指向虚函数表的指针
这个指针用于支持方法的动态分派,类似于C++中的虚函数机制
__ptrauth_objc_isa_pointer 是一个指针认证标记,增强安全性
*/
const struct dispatch_queue_attr_vtable_s *__ptrauth_objc_isa_pointer do_vtable;

/**
强引用计数
用于跟踪对象的引用次数,确保对象在被引用时不会被释放
*/
int do_ref_cnt;

/**
弱引用计数
用于管理对象的弱引用关系,允许对象在没有强引用时被释放
*/
int do_xref_cnt;
};

2、返回值 dispatch_queue_t

dispatch_queue_create 的返回值是 dispatch_queue_tdispatch_queue_t 是一个指向 dispatch_queue_s 结构体的指针:

1
typedef struct dispatch_queue_s *dispatch_queue_t;

dispatch_queue_s 是一个复杂的结构体,包含了许多宏定义和其他结构体的嵌套。完全展开后的 dispatch_queue_s 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
struct dispatch_queue_s {
// 作为基础对象的一部分,允许类型转换
struct dispatch_object_s _as_do[0];

// 内部对象头部定义
struct _os_object_s _as_os_obj[0]; // 零长度数组,用于类型转换
const struct dispatch_queue_vtable_s *__ptrauth_objc_isa_pointer do_vtable; // 虚表指针,用于方法调度
int volatile do_ref_cnt; // 对象的引用计数
int volatile do_xref_cnt; // 交叉引用计数

struct dispatch_queue_s *volatile do_next; // 下一个队列对象的指针

struct dispatch_queue_s *do_targetq; // 目标队列指针
void *do_ctxt; // 上下文指针
union {
dispatch_function_t DISPATCH_FUNCTION_POINTER do_finalizer; // 终结器函数
void *do_introspection_ctxt; // 自省上下文
};

void *__dq_opaque1; // 指针大小的字段

// 32位整数,表示队列的状态
union {
uint64_t volatile dq_state; // 队列状态
dispatch_lock dq_state_lock; // 队列状态锁
uint32_t dq_state_bits; // 队列状态位
};

// LP64 全局队列缓存行边界
unsigned long dq_serialnum; // 队列的序列号
const char *dq_label; // 队列标签,用于标识队列

// 联合体,用于存储队列的标志和宽度
union {
uint32_t volatile dq_atomic_flags; // 队列的原子标志
struct {
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; // Mach 消息接收引用
struct dispatch_channel_callbacks_s const *dch_callbacks; // 通道回调
};

int volatile dq_sref_cnt; // 队列的强引用计数
} __attribute__((aligned(8))); // 结构体按 8 字节对齐

dispatch_queue_s 中,有多个匿名联合体,例如对于其中的:

1
2
3
4
5
6
7
8
// 联合体,用于存储队列的标志和宽度
union {
uint32_t volatile dq_atomic_flags; // 队列的原子标志
struct {
const uint16_t dq_width; // 队列的宽度,表示并发能力
const uint16_t __dq_opaque2; // 额外的占位符字段
};
};

匿名联合体中还有个匿名结构体,该数据结构总结如下:

  • 联合体(union
    • 联合体中的所有成员共享同一段内存。
    • dq_atomic_flags 和下面的匿名结构体共享同一段内存,对 dq_atomic_flags 赋值实际上就是对下面的结构体赋值。
    • dq_atomic_flags32 位的无符号整数类型,其占用了 32 位的内存区域。
  • 匿名结构体
    • 上述源码中结构体是匿名的,包含两个 uint16_t 类型的成员:dq_width__dq_opaque2,即各自分别占用 16 位内存。
    • 由于结构体的成员在内存中的排序是按照它们声明的顺序依次排列的,所以 dq_width__dq_opaque2 分别占用 dq_atomic_flags 的低 16 位和高 16 位。

3、_dispatch_lane_create_with_target

再回头看下 dispatch_queue_create 函数的定义:

1
2
3
4
5
6
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 函数。

其中第三个参数 DISPATCH_TARGET_QUEUE_DEFAULT 本质是个 NULL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*!
* @const DISPATCH_TARGET_QUEUE_DEFAULT
* @discussion Constant to pass to the dispatch_queue_create_with_target(),
* dispatch_set_target_queue() and dispatch_source_create() functions to
* indicate that the default target queue for the object type in question
* should be used.
*/
#define DISPATCH_TARGET_QUEUE_DEFAULT NULL
```c
`_dispatch_lane_create_with_target` 实现源码如下:
```c
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

// 生成 dqai:将传入的队列属性转换为结构体 dispatch_queue_attr_info_t 类型的 dqai
// dqai 中包含了队列的服务质量(QoS)、相对优先级、超额提交属性、自动释放频率、并发性和非活动状态等信息。
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

// 提取服务质量(QoS)信息。
// 服务质量(QoS)是指任务的优先级,影响任务调度的优先级和资源分配。
dispatch_qos_t qos = dqai.dqai_qos;



#if !HAVE_PTHREAD_WORKQUEUE_QOS
// 如果不支持 pthread 的 QoS,并且 QoS 是用户交互级别,则降级为用户发起级别。
// 用户交互级别:用于需要立即响应用户交互的任务,优先级最高。
// 用户发起级别:用于用户主动发起的任务,优先级高于默认级别。
if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
}
// 如果 QoS 是维护级别,则降级为后台级别。
// 维护级别:用于系统维护任务,优先级低。
// 后台级别:用于后台运行的任务,优先级低于默认级别。
if (qos == DISPATCH_QOS_MAINTENANCE) {
dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
}
#endif // !HAVE_PTHREAD_WORKQUEUE_QOS





// 2、创建 tq

// 提取超额提交属性。
// 超额提交:允许队列超出系统建议的并发限制,可能会导致资源争用。
_dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
// 如果指定了超额提交并且有目标队列。
if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {
// 如果目标队列不是全局队列,则崩溃。
// 目标队列:队列的执行目标队列,可以是全局队列或其他队列。
if (tq->do_targetq) {
DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
"a non-global target queue");
}
}


// 如果目标队列是全局根队列。
// 根队列:GCD 提供的全局并发队列,系统级别的队列。
if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
// 处理属性和目标队列之间的差异,以属性为准。
if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
overcommit = _dispatch_queue_attr_overcommit_enabled;
} else {
overcommit = _dispatch_queue_attr_overcommit_disabled;
}
}
// 如果 QoS 未指定,则从目标队列中获取。
if (qos == DISPATCH_QOS_UNSPECIFIED) {
qos = _dispatch_priority_qos(tq->dq_priority);
}
tq = NULL; // 清除目标队列引用。
} else if (tq && _dispatch_queue_is_cooperative(tq)) {
// 如果目标队列是协作型根队列,则崩溃(未实现)。
// 协作型根队列:一种特殊的根队列,尚未实现。
DISPATCH_CLIENT_CRASH(tq, "Cannot target object to cooperative root queue - not implemented");
} else if (tq && !tq->do_targetq) {
// 如果目标是 pthread 或 runloop 根队列,不允许设置 QoS 或超额提交。
if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
"and use this kind of target queue");
}
} else {
// 如果未指定超额提交,串行队列默认开启超额提交。即:串行队列是 overcommit 的,并发队列是非 overcommit 的
if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
overcommit = dqai.dqai_concurrent ?
_dispatch_queue_attr_overcommit_disabled :
_dispatch_queue_attr_overcommit_enabled;
}
}



// 如果没有目标队列,根据 QoS 获取根队列。
// dispatch_queue_create 函数,tq 参数是 DISPATCH_TARGET_QUEUE_DEFAULT 宏,即 NULL
if (!tq) {
uintptr_t flags = (overcommit == _dispatch_queue_attr_overcommit_enabled) ? DISPATCH_QUEUE_OVERCOMMIT : 0;
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");
}
}




// 3、初始化队列 dq

// dispatch_queue_create 函数,legacy 参数传的 true
if (legacy) {
// 如果指定了非传统属性,则使用非传统类。
// 非传统属性:例如非活动状态或自动释放频率。
// 非传统类:指不使用传统的队列类。
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_queue_concurrent_vtable
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
// 宏展开后:vtable = &_dispatch_queue_serial_vtable
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) {
// 调用 _dispatch_strdup_if_mutable 函数创建标签的副本
// _dispatch_strdup_if_mutable 函数总是调用 strdup 创建一个新的字符串副本
const char *tmp = _dispatch_strdup_if_mutable(label);

// 检查新创建的副本(tmp)是否与原始标签(label)不同
// 如果 tmp != label,说明创建了一个新的字符串副本,内存地址不同
if (tmp != label) {
// 设置 DQF_LABEL_NEEDS_FREE 标志,表示需要在适当的时候释放这个标签
// 这样做是为了在队列销毁时能够正确释放标签内存,避免内存泄漏
dqf |= DQF_LABEL_NEEDS_FREE;

// 将标签变量更新为新创建的副本
// 这样后续操作将使用这个新的标签副本,而不是原始标签
label = tmp;
}
}


// 3.1 分配内存
// 注意,dq 是 dispatch_lane_t 类型
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));

// 3.2 初始化
_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);
// 设置目标 targetq 为 tq
dq->do_targetq = tq;
// 调试输出。
_dispatch_object_debug(dq, "%s", __func__);
// 返回创建的队列。
return _dispatch_trace_queue_create(dq)._dq;
}

上述逻辑比较多,可以将上面逻辑拆分成下面这部分:

  • 生成 dqaidispatch_queue_attr_info_t
  • 创建 tqdispatch_queue_t
  • 初始化队列 dqdispatch_lane_t
    • 分配内存
    • 初始化
  • dq 其他配置

接下来,基于源码分别看下上述各部分逻辑。

(1)生成 dqai

dqaidispatch_queue_attr_info_t 结构体类型,dqai 中包含了队列的服务质量(QoS)、相对优先级、超额提交属性、自动释放频率、并发性和非活动状态等信息。

dispatch_queue_attr_info_t 结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typedef struct dispatch_queue_attr_info_s {
// 服务质量(QoS),占用 8 位。
// QoS 决定了任务的优先级,影响调度和资源分配。
dispatch_qos_t dqai_qos : 8;

// 相对优先级,占用 8 位。
// 这是相对于服务质量(QoS)的优先级调整值,用于进一步微调任务的调度优先级。
int dqai_relpri : 8;

// 超额提交属性,占用 2 位。
// 指示队列是否允许超出系统建议的并发限制。
// 可能的值可以表示启用、禁用或未指定。
uint16_t dqai_overcommit : 2;

// 自动释放频率,占用 2 位。
// 控制自动释放池的刷新频率,可能的值包括从不、每个工作项或默认。
uint16_t dqai_autorelease_frequency : 2;

// 并发性属性,占用 1 位。
// 指示队列是并发队列(多个任务可以同时执行)还是串行队列(任务按顺序执行)。
uint16_t dqai_concurrent : 1;

// 非活动状态,占用 1 位。
// 指示队列是否在初始化时处于非活动状态。
// 非活动队列在激活之前不会调度任务。
uint16_t dqai_inactive : 1;
} dispatch_queue_attr_info_t;

生成 dqai 是通过 _dispatch_queue_attr_to_info 函数实现的,_dispatch_queue_attr_to_info 函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
dispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
// 初始化一个队列属性信息结构体,用于存储解析后的队列属性。
// 这个结构体包含了服务质量(QoS)、相对优先级、超额提交设置、自动释放频率、并发性和非活动状态等信息。
dispatch_queue_attr_info_t dqai = { };

// 如果传入的队列属性为空,则返回默认初始化的结构体。
if (!dqa) return dqai;

// 检查传入的队列属性是否在有效范围内。
// 如果不在范围内,则崩溃,因为这意味着传入了无效的队列属性。
if (dqa < _dispatch_queue_attrs ||
dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) {
DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
}

// 计算索引值,用于解析不同的属性。
// 这里使用的是一个偏移量计算方法,根据不同的属性组合得出唯一的索引。
size_t idx = (size_t)(dqa - _dispatch_queue_attrs);

// 解析非活动状态属性。
// 通过索引对非活动状态的总数取模,判断该属性是否设置。
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 = -(int)(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
// 更新索引,准备解析下一个属性。
idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;

// 解析服务质量(QoS)属性。
// 通过索引对 QoS 属性的总数取模,获取 QoS 值。
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;

// 返回解析后的队列属性信息结构体。
return dqai;
}

前面已经提到,当创建串行队列时,attr 参数为 DISPATCH_QUEUE_SERIAL,本质为 NULL。此时 _dispatch_queue_attr_to_info 函数返回值为:

1
2
3
4
5
6
7
8
dispatch_queue_attr_info_t dqai = {
.dqai_qos = 0,
.dqai_relpri = 0,
.dqai_overcommit = 0,
.dqai_autorelease_frequency = 0,
.dqai_concurrent = 0, // 串行队列
.dqai_inactive = 0
};

当创建并发队列时,attr 参数为 DISPATCH_QUEUE_CONCURRENT,此时 _dispatch_queue_attr_to_info 函数返回值为:

1
2
3
4
5
6
7
8
dispatch_queue_attr_info_t dqai = {
.dqai_qos = 0,
.dqai_relpri = 0,
.dqai_overcommit = 0,
.dqai_autorelease_frequency = 0,
.dqai_concurrent = 1, // 并发队列
.dqai_inactive = 0
};

所以,当创建串行、并发队列时,_dispatch_queue_attr_to_info 函数返回的结果仅 dqai_concurrent 值不同,分别是 01

(2)创建 tq

根据前述源码,在创建 tq 前需要先获取超额提交属性 overcommit
dispatch_queue_create 函数在调用时 _dispatch_lane_create_with_targettq 参数传入的是 DISPATCH_TARGET_QUEUE_DEFAULT,前面已经提到,DISPATCH_TARGET_QUEUE_DEFAULT 本质是个 NULL,所以会走调用下面的 if 分支:

1
2
3
4
5
if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
overcommit = dqai.dqai_concurrent ?
_dispatch_queue_attr_overcommit_disabled :
_dispatch_queue_attr_overcommit_enabled;
}

所以,创建串行队列时,overcommit 为 _dispatch_queue_attr_overcommit_enabled。创建并发队列时,overcommit_dispatch_queue_attr_overcommit_enabled

从此处可以得到一个结论:串行队列是 overcommit 的,并发队列是非 overcommit 的。

接下来进入 tq 的创建逻辑:

1
2
3
4
5
6
7
8
9
10
11
// 如果没有目标队列,根据 QoS 获取根队列。
// dispatch_queue_create 函数,tq 参数是 DISPATCH_TARGET_QUEUE_DEFAULT 宏,即 NULL
if (!tq) {
uintptr_t flags = (overcommit == _dispatch_queue_attr_overcommit_enabled) ? DISPATCH_QUEUE_OVERCOMMIT : 0;
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");
}
}

其中,_dispatch_get_root_queue 实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, uintptr_t flags)
{
// 检查 QoS 是否在有效范围内。如果不在范围内,则触发崩溃。
if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
}

unsigned int add_on = 0; // 初始化索引偏移量为0

// 根据标志设置索引偏移量
if (flags & DISPATCH_QUEUE_OVERCOMMIT) {
// 如果设置了 OVERCOMMIT 标志,使用 OVERCOMMIT 偏移量
add_on = DISPATCH_ROOT_QUEUE_IDX_OFFSET_OVERCOMMIT;
} else if (flags & DISPATCH_QUEUE_COOPERATIVE) {
// 如果设置了 COOPERATIVE 标志,使用 COOPERATIVE 偏移量
add_on = DISPATCH_ROOT_QUEUE_IDX_OFFSET_COOPERATIVE;
}

// 计算并返回指向合适的全局根队列的指针
// 计算公式为:3 * (qos - 1) + add_on
// 其中,3 是每个 QoS 级别有三个不同类型的队列(普通、超额提交、协作)
return &_dispatch_root_queues[3 * (qos - 1) + add_on];
}

这里先看下 _dispatch_root_queues_dispatch_root_queues 是一个 dispatch_queue_global_s 类型的数组,先看下 dispatch_queue_global_s 的定义,dispatch_queue_global_s 的定义涉及击到一系列宏,将其完全展开后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
struct dispatch_queue_global_s {
// 零长度数组,用于类型转换为 dispatch_queue_s
struct dispatch_queue_s _as_dq[0];

// Dispatch 对象头部定义
struct dispatch_object_s _as_do[0]; // 零长度数组,用于类型转换为 dispatch_object_s
struct _os_object_s _as_os_obj[0]; // 零长度数组,用于类型转换为 _os_object_s

// 对象的虚表指针,用于方法调度
const struct dispatch_lane_vtable_s *__ptrauth_objc_isa_pointer do_vtable;

// 对象的引用计数
int volatile do_ref_cnt;

// 交叉引用计数
int volatile do_xref_cnt;

// 下一个队列对象的指针
struct dispatch_lane_s *volatile do_next;

// 目标队列指针
struct dispatch_queue_s *do_targetq;

// 上下文指针
void *do_ctxt;

// 联合体,包含终结器函数或自省上下文
union {
dispatch_function_t DISPATCH_FUNCTION_POINTER do_finalizer;
void *do_introspection_ctxt;
};

// 指针大小的字段,指向队列尾部的对象
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;

// 联合体,用于存储队列的状态
union {
uint64_t volatile dq_state; // 队列状态
dispatch_lock dq_state_lock; // 队列状态锁
uint32_t dq_state_bits; // 队列状态位
};

// LP64 全局队列缓存行边界
unsigned long dq_serialnum; // 队列的序列号

// 队列标签,用于标识队列
const char *dq_label;

// 联合体,用于存储队列的标志和宽度
union {
uint32_t volatile dq_atomic_flags; // 队列的原子标志
struct {
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; // Mach 消息接收引用
struct dispatch_channel_callbacks_s const *dch_callbacks; // 通道回调
};

// 队列的强引用计数
int volatile dq_sref_cnt;
} __attribute__((__aligned__(64))); // 结构体按 64 字节对齐

可以看到,dispatch_queue_global_s 与前面提到的 dispatch_queue_s 结构几乎是一样的,最大的区别是 dispatch_queue_global_s 中多了个 struct dispatch_queue_s _as_dq[0]; 的零长数组,意味着可以通过 _as_dqdispatch_queue_global_s 转成 dispatch_queue_s

这种设计在 libdispatch 的源码中非常常见,通常用于类型转换或类型兼容性,这种设计的原理是:

  • 内存布局和对齐
    • 在 C 语言中,结构体的内存布局是由其成员的声明顺序决定的。编译器会按照声明的顺序为每个成员分配内存,并根据目标平台的对齐要求进行对齐。
    • 零长度数组在内存中不占用空间,但它的存在可以影响后续成员的对齐和布局。
  • 类型转换的目的
    • 当一个结构体包含另一个结构体的零长度数组时,编译器会将该数组视作一个指向数组元素类型的指针。这意味着,在内存布局上,该结构体的起始位置与零长度数组的元素类型的起始位置一致。
    • 通过这种方式,包含零长度数组的结构体可以与数组元素类型具有相同的内存起始布局,从而实现类型兼容。
  • 类型兼容性的实现
    • 例如,dispatch_queue_global_s 包含 struct dispatch_queue_s _as_dq[0],这意味着在内存布局上,dispatch_queue_global_s 的起始位置与 dispatch_queue_s 的起始位置一致。
    • 这允许通过 _as_dq 指针将 dispatch_queue_global_s 视作 dispatch_queue_s,因为它们在内存中的起始位置是相同的。
  • 实现多态性
    • 这种设计模式允许使用不同类型的结构体通过相同的接口进行操作,实现多态性和灵活性。
    • 通过零长度数组或灵活数组成员,开发者可以在不增加内存开销的情况下,实现类型之间的灵活转换。

_dispatch_root_queues 的定义中也涉及到一系列宏定义,将 _dispatch_root_queues 内部宏定义完全展开后,实际内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
struct dispatch_queue_global_s _dispatch_root_queues[] = {
// MAINTENANCE QOS
[DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_MAINTENANCE, 0),
.dq_label = "com.apple.root.maintenance-qos",
.dq_serialnum = 4,
},
[DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS_OVERCOMMIT] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS_OVERCOMMIT),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_MAINTENANCE, 0) | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.maintenance-qos.overcommit",
.dq_serialnum = 5,
},
[DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS_COOPERATIVE] = {
.do_vtable = DISPATCH_VTABLE(queue_concurrent),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_MAINTENANCE, 0) | DISPATCH_PRIORITY_FLAG_COOPERATIVE,
.dq_label = "com.apple.root.maintenance-qos.cooperative",
.dq_serialnum = 6,
},
// BACKGROUND QOS
[DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_BACKGROUND, 0),
.dq_label = "com.apple.root.background-qos",
.dq_serialnum = 7,
},
[DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS_OVERCOMMIT] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS_OVERCOMMIT),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_BACKGROUND, 0) | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.background-qos.overcommit",
.dq_serialnum = 8,
},
[DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS_COOPERATIVE] = {
.do_vtable = DISPATCH_VTABLE(queue_concurrent),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_BACKGROUND, 0) | DISPATCH_PRIORITY_FLAG_COOPERATIVE,
.dq_label = "com.apple.root.background-qos.cooperative",
.dq_serialnum = 9,
},
// UTILITY QOS
[DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_UTILITY, 0),
.dq_label = "com.apple.root.utility-qos",
.dq_serialnum = 10,
},
[DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS_OVERCOMMIT] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS_OVERCOMMIT),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_UTILITY, 0) | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.utility-qos.overcommit",
.dq_serialnum = 11,
},
[DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS_COOPERATIVE] = {
.do_vtable = DISPATCH_VTABLE(queue_concurrent),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_UTILITY, 0) | DISPATCH_PRIORITY_FLAG_COOPERATIVE,
.dq_label = "com.apple.root.utility-qos.cooperative",
.dq_serialnum = 12,
},
// DEFAULT QOS
[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make_fallback(DISPATCH_QOS_DEFAULT),
.dq_label = "com.apple.root.default-qos",
.dq_serialnum = 13,
},
[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make_fallback(DISPATCH_QOS_DEFAULT) | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.default-qos.overcommit",
.dq_serialnum = 14,
},
[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_COOPERATIVE] = {
.do_vtable = DISPATCH_VTABLE(queue_concurrent),
.dq_priority = _dispatch_priority_make_fallback(DISPATCH_QOS_DEFAULT) | DISPATCH_PRIORITY_FLAG_COOPERATIVE,
.dq_label = "com.apple.root.default-qos.cooperative",
.dq_serialnum = 15,
},
// USER_INITIATED QOS
[DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_USER_INITIATED, 0),
.dq_label = "com.apple.root.user-initiated-qos",
.dq_serialnum = 16,
},
[DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS_OVERCOMMIT] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS_OVERCOMMIT),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_USER_INITIATED, 0) | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-initiated-qos.overcommit",
.dq_serialnum = 17,
},
[DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS_COOPERATIVE] = {
.do_vtable = DISPATCH_VTABLE(queue_concurrent),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_USER_INITIATED, 0) | DISPATCH_PRIORITY_FLAG_COOPERATIVE,
.dq_label = "com.apple.root.user-initiated-qos.cooperative",
.dq_serialnum = 18,
},
// USER_INTERACTIVE QOS
[DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_USER_INTERACTIVE, 0),
.dq_label = "com.apple.root.user-interactive-qos",
.dq_serialnum = 19,
},
[DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS_OVERCOMMIT] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS_OVERCOMMIT),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_USER_INTERACTIVE, 0) | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-interactive-qos.overcommit",
.dq_serialnum = 20,
},
[DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS_COOPERATIVE] = {
.do_vtable = DISPATCH_VTABLE(queue_concurrent),
.dq_priority = _dispatch_priority_make(DISPATCH_QOS_USER_INTERACTIVE, 0) | DISPATCH_PRIORITY_FLAG_COOPERATIVE,
.dq_label = "com.apple.root.user-interactive-qos.cooperative",
.dq_serialnum = 21,
},
};

根据源码可知,_dispatch_root_queues 本质就是一个数组,数组的 index 是个枚举,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum {
DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS = 0,
DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS_OVERCOMMIT,
DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS_COOPERATIVE,
DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS,
DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS_OVERCOMMIT,
DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS_COOPERATIVE,
DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS,
DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS_OVERCOMMIT,
DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS_COOPERATIVE,
DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS,
DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT,
DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_COOPERATIVE,
DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS,
DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS_OVERCOMMIT,
DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS_COOPERATIVE,
DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS,
DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS_OVERCOMMIT,
DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS_COOPERATIVE,
_DISPATCH_ROOT_QUEUE_IDX_COUNT,
};

根据枚举定义也可以看出,数组的 index 是通过不同的调度策略进行拼接的,_dispatch_get_root_queue 中会根据不同的策略计算出对应的 index_dispatch_root_queues 数组中取值。

再回到 _dispatch_get_root_queue 函数,看创建串行、并发队列时,传入的 index,经过计算可以知道,创建串行队列时,index = 10,创建并发队列时 index = 9

所以创建串行队列时,返回的是 _dispatch_root_queues 数组 index = 10 的元素(DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT),即:

1
2
3
4
5
6
7
8
9
[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make_fallback(DISPATCH_QOS_DEFAULT) | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.default-qos.overcommit",
.dq_serialnum = 14,
},

创建并发队列时,返回的是 _dispatch_root_queues 数组 index = 9 的元素(DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS),即:

1
2
3
4
5
6
7
8
9
[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS] = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
.do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS),
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
.dq_priority = _dispatch_priority_make_fallback(DISPATCH_QOS_DEFAULT),
.dq_label = "com.apple.root.default-qos",
.dq_serialnum = 13,
},

这里创建完成的 tq 会在前述的 _dispatch_lane_create_with_target 后续逻辑中被设置成队列的 do_targetq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{

// ......

if (!tq) {
uintptr_t flags = (overcommit == _dispatch_queue_attr_overcommit_enabled) ? DISPATCH_QUEUE_OVERCOMMIT : 0;
tq = _dispatch_get_root_queue(
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,
flags)->_as_dq;
// ......
}

// ...

dq->do_targetq = tq;

// ...

return _dispatch_trace_queue_create(dq)._dq;
}

所以对于自定义的队列,无论是串行队列还是并发队列,其 do_targetq 都是从根队列数组 _dispatch_root_queues 中取出的对应的根队列(root queue)。

(3)初始化队列 dq(dispatch_lane_t)

_dispatch_lane_create_with_target 函数中,创建的 dqdispatch_lane_t 类型,那 dispatch_lane_t 和前面提到的 dispatch_queue_s 类型是什么关系呢?

dispatch_lane_t 是一个指向 dispatch_lane_s 结构体的指针类型。dispatch_lane_sdispatch_queue_s 的子类型,继承了队列的基本功能,并扩展了用于任务调度的字段:

1
2
3
4
5
6
7
8
9
struct dispatch_lane_s {
// ......

struct dispatch_object_s *dq_items_head; // 队列头
struct dispatch_object_s *dq_items_tail; // 队列尾
dispatch_unfair_lock_s dq_sidelock; // 辅助锁

// dispatch_queue_s 包含的其他字段 ......
};

_dispatch_lane_create_with_target 函数实现上来看,如果创建的串行队列还是并发队列,返回的都是 dispatch_lane_s 类型。

初始化队列 dq 可以分成两部分逻辑来看:

  • 分配内存
  • 初始化
a、分配内存

分配内存主要是通过 _dispatch_object_alloc 函数实现的:

1
2
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));

其中,vtable 参数值通过下述逻辑获取的:

1
2
3
4
5
6
7
if (dqai.dqai_concurrent) {
// 宏展开后:vtable = &_dispatch_queue_concurrent_vtable
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
// 宏展开后:vtable = &_dispatch_queue_serial_vtable
vtable = DISPATCH_VTABLE(queue_serial);
}

vtable 实际上是一个虚表(虚函数表),虚表主要用于支持多态性和动态方法调用,允许对象在运行时调用对应的函数实现。一个类中有虚函数(virtual 关键字声明的函数)时,编译器会自动为该类及其派生类自动生成虚表,虚表中的指针会指向派生类的实现。

可以简单理解为虚表中存储了函数具体实现的指针。对应的类或结构体,通过访问虚表中的函数指针可以调用到对应的函数实现。

由上述源码可以知道,当创建串行队列时,vtable&_dispatch_queue_serial_vtable,当创建并发队列时,vtable 为 &_dispatch_queue_concurrent_vtable

接下来以 _dispatch_queue_concurrent_vtable 为例,看下源码实现。从 libdispatch 的源码中,找到了如下宏:

1
DISPATCH_SUBCLASS_DECL(queue_concurrent, queue, lane);

根据源码,将宏完全展开后如下:

1
2
3
4
5
6
7
8
9
@protocol OS_dispatch_queue_concurrent <OS_dispatch_queue>
@end

@interface OS_dispatch_queue_concurrent () <OS_dispatch_queue_concurrent>
@end

struct dispatch_queue_concurrent_s;
extern const struct dispatch_lane_vtable_s _OS_dispatch_queue_concurrent_vtable;
extern const struct dispatch_lane_vtable_s _dispatch_queue_concurrent_vtable __asm__("__OS_dispatch_queue_concurrent_vtable");

从上述源码可以看出,_OS_dispatch_queue_concurrent_vtableOS_dispatch_queue_concurrent 类的虚表,之后又通过 __asm__ 指令在汇编代码中将 _OS_dispatch_queue_concurrent_vtable_dispatch_queue_concurrent_vtable 进行关联。

所以 _dispatch_queue_concurrent_vtable 对应的实现类是 OS_dispatch_queue_concurrent,而 _dispatch_queue_concurrent_vtable 中则存储了 OS_dispatch_queue_concurrent 类相关函数的实现的指针。

这也是为什么将创建的并发队列打印出来之后,显示 OS_dispatch_queue_concurrent 的原因:

同样的,根据源码可以知道创建串行队列,对应的实现类是 OS_dispatch_queue_serial

从 libdispatch 的源码的下述源码,可以看到 _dispatch_queue_concurrent_vtable 中存储的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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,
);

#define DISPATCH_VTABLE_SUBCLASS_INSTANCE(name, ctype, ...) \
OS_OBJECT_VTABLE_SUBCLASS_INSTANCE(dispatch_##name, dispatch_##ctype, \
_dispatch_xref_dispose, _dispatch_dispose, \
.do_kind = #name, __VA_ARGS__)

#define OS_OBJECT_VTABLE_SUBCLASS_INSTANCE(name, ctype, xdispose, dispose, ...) \
const struct ctype##_vtable_s OS_OBJECT_CLASS_SYMBOL(name) = { \
._os_obj_xref_dispose = xdispose, \
._os_obj_dispose = dispose, \
._os_obj_vtable = { __VA_ARGS__ }, \
}

#define OS_OBJECT_CLASS_SYMBOL(name) _##name##_vtable

将宏完全展开后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const struct dispatch_lane_vtable_s _dispatch_queue_concurrent_vtable = {
._os_obj_xref_dispose = _dispatch_xref_dispose,
._os_obj_dispose = _dispatch_dispose,
._os_obj_vtable = {
.do_kind = "queue_concurrent",
.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_queue_serial_vtable 内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const struct dispatch_lane_vtable_s _dispatch_queue_serial_vtable = {
._os_obj_xref_dispose = _dispatch_xref_dispose,
._os_obj_dispose = _dispatch_dispose,
._os_obj_vtable = {
.do_kind = "queue_serial",
.do_type = DISPATCH_QUEUE_SERIAL_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_push,
},
};

其中存储的各个函数实现在 libdispatch 的源码中都可以找到,这里就不一一贴出来了。

再回头看下分配内存逻辑:

1
2
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));

其中 _dispatch_object_alloc 函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
return _os_object_alloc_realized(vtable, size);
}

// 分配并初始化一个 _os_object_t 类型的对象
inline _os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
// 定义一个 _os_object_t 类型的变量 obj,用于存储分配的对象
_os_object_t obj;

// 断言检查,确保传入的 size 大于等于 _os_object_s 结构体的大小
// 这是为了确保分配的内存足够大,能够包含 _os_object_s 结构体的所有字段
dispatch_assert(size >= sizeof(struct _os_object_s));

// 使用 while 循环尝试分配内存,直到分配成功为止
// unlikely 宏用于提示编译器,这个条件(分配失败)不太可能发生,从而优化分支预测
while (unlikely(!(obj = calloc(1u, size)))) {
// 如果内存分配失败,调用 _dispatch_temporary_resource_shortage 函数处理资源短缺
_dispatch_temporary_resource_shortage();
}

// 设置分配的对象的 isa 指针为传入的 cls,表示对象的类型
obj->os_obj_isa = cls;

// 返回分配并初始化好的对象
return obj;
}

上述主要逻辑如下:

  • 创建 _os_object_t 类型的对象 obj,并分配内存。
  • vtable 赋值给 obj->os_obj_isa
    • vtable 是虚表,存储了具体的函数实现的指针。
    • 串行队列时,vtable 虚表对应实现类是 OS_dispatch_queue_serial
      • 并发队列时,vtable 虚表对应实现类是 OS_dispatch_queue_concurrent
b、初始化

初始化是通过调用 _dispatch_queue_init 函数实现的:

1
2
3
_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_init 传入了 4 个参数,根据源码可知,创建串行队列时,第 3 个参数(width)传入的十进制值是 1;

创建并发队列时,第 3 个参数(width)传入的十进制值是 DISPATCH_QUEUE_WIDTH_MAX,DISPATCH_QUEUE_WIDTH_MAX 是个宏:

1
2
#define DISPATCH_QUEUE_WIDTH_FULL           0x1000ull
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2)

经过计算,DISPATCH_QUEUE_WIDTH_MAX 的十进制值是 4094,所以创建串行队列时,第 3 个参数(width)传入的十进制值是 4094

结合前面生成 dqai 那部分的源码,可以知道无论串行队列还是并发队列,dqai.dqai_inactive 值都是 0,所以,_dispatch_queue_init 函数第 4 个参数(initial_state_bits)传入的值是 DISPATCH_QUEUE_ROLE_INNER0x0000000000000000ull)。

_dispatch_queue_init 函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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 的 _dispatch_lane_resume
// 如果是调度源类型,进一步增加引用计数
if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {
dq->do_ref_cnt++; // 当 DSF_DELETED 被设置时释放
}
}

// 将初始状态位合并到队列状态中,由于传入的 initial_state_bits 参数为 DISPATCH_QUEUE_ROLE_INNER(0x0000000000000000ull),所以这一步对 dq_state 的值没有任何影响
dq_state |= initial_state_bits;
// 将队列的下一个对象指针设置为无列表状态
dq->do_next = DISPATCH_OBJECT_LISTLESS;
// 将宽度标志合并到队列标志中
dqf |= DQF_WIDTH(width);
// 原子存储队列的标志,使用 relaxed 内存顺序
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;
}

这部分逻辑,重点需要关注 dq_statedq_width 的赋值。

dq_state 的赋值比较好理解,其初始值就是:

1
dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width);

注意,这里的 dq_state 非最终值,后续还有 dq_state 赋值逻辑,在下面“dq 其他配置”部分会继续分析 dq_state

dq_width 字段是队列的宽度,存储了队列能够同时处理的任务的数量。关于 dq_width 的赋值,先回头看下 dispatch_queue_t 的数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef struct dispatch_queue_s *dispatch_queue_t;

struct dispatch_queue_s {

// 略 ......

struct dispatch_queue_s *volatile do_next; // 下一个队列对象的指针

struct dispatch_queue_s *do_targetq; // 目标队列指针

// 32位整数,表示队列的状态
union {
uint64_t volatile dq_state; // 队列状态
dispatch_lock dq_state_lock; // 队列状态锁
uint32_t dq_state_bits; // 队列状态位
};

// LP64 全局队列缓存行边界
unsigned long dq_serialnum; // 队列的序列号
const char *dq_label; // 队列标签,用于标识队列

// 联合体,用于存储队列的标志和宽度
union {
uint32_t volatile dq_atomic_flags; // 队列的原子标志
struct {
const uint16_t dq_width; // 队列的宽度,即:队列能够同时处理的任务的数量
const uint16_t __dq_opaque2; // 额外的占位符字段
};
};

// 略 ......

}

前面已经提到,联合体中的 dq_atomic_flags 是 32 位的无符号整数类型,其占用了 32 位的内存区域,联合体内部 dq_width__dq_opaque2 分别占用 dq_atomic_flags 的低 16 位和高 16 位。

_dispatch_queue_init 函数中,注意下面这部分逻辑:

1
2
dqf |= DQF_WIDTH(width);
os_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed);

其中 DQF_WIDTH 宏定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
DISPATCH_OPTIONS(dispatch_queue_flags, uint32_t,
DQF_NONE = 0x00000000,
DQF_AUTORELEASE_ALWAYS = 0x00010000,
DQF_AUTORELEASE_NEVER = 0x00020000,

#define _DQF_AUTORELEASE_MASK 0x00030000 // 自动释放相关的掩码

DQF_THREAD_BOUND = 0x00040000, // 队列与线程绑定标志
DQF_BARRIER_BIT = 0x00080000, // 队列在其目标上是一个屏障
DQF_TARGETED = 0x00100000, // 队列被其他对象作为目标
DQF_LABEL_NEEDS_FREE = 0x00200000,
DQF_MUTABLE = 0x00400000,
DQF_RELEASED = 0x00800000, // 引用计数为 -1,表示已释放

DSF_STRICT = 0x04000000,
DSF_WLH_CHANGED = 0x08000000,
DSF_CANCELED = 0x10000000,
DSF_CANCEL_WAITER = 0x20000000,
DSF_NEEDS_EVENT = 0x40000000,
DSF_DELETED = 0x80000000,

#define DQF_FLAGS_MASK ((dispatch_queue_flags_t)0xffff0000) // 标志位掩码
#define DQF_WIDTH_MASK ((dispatch_queue_flags_t)0x0000ffff) // 宽度掩码
#define DQF_WIDTH(n) ((dispatch_queue_flags_t)(uint16_t)(n)) // 设置队列宽度
);

DQF_WIDTH 宏将 width 转换为 dispatch_queue_flags_t 类型,并确保它位于低 16 位。具体来说,DQF_WIDTH(width) 通过将 width 强制转换为 uint16_t,然后再转换为 dispatch_queue_flags_t,确保宽度值仅影响低 16 位。

也就是说,下述逻辑:

1
dqf |= DQF_WIDTH(width);

width 的值设置到 dqf 的低 16 位上。

os_atomic_store2o 宏定义如下:

1
2
3
4
5
#define os_atomic_store2o(p, f, v, m) \
os_atomic_store(&(p)->f, (v), m)

#define os_atomic_store(p, v, m) \
atomic_store_explicit(_os_atomic_c11_atomic(p), v, memory_order_##m)

可以定义知道 os_atomic_store2o 宏的作用是将值 dqf 原子地存储到结构体 dq 的字段 dq_atomic_flags 中。

所以,最终 width 值将会赋值给 dispatch_queue_s 中的 dq_width 上,dqf 其他内容存储到 __dq_opaque2 中。

所以最终:

  • 创建串行队列时,dq_width = 1
  • 创建并发队列时,dq_width = 4094

(4)dq 其他配置

这部分逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 设置队列标签和优先级。
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);
// 设置目标 targetq 为当前 dispatch_queue_t
dq->do_targetq = tq;

这里主要是对 dq 其他的一些参数的配置,这里重点看下 _dispatch_lane_inherit_wlh_from_target 函数的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static void
_dispatch_lane_inherit_wlh_from_target(dispatch_lane_t dq, dispatch_queue_t tq)
{
uint64_t old_state, new_state, role;

// 检查目标队列 tq 是否具有 QUEUE_ROOT 类型标志
if (!dx_hastypeflag(tq, QUEUE_ROOT)) {
// 如果 tq 不是根队列,将 role 设置为 INNER(内部队列)
role = DISPATCH_QUEUE_ROLE_INNER;
} else if (_dispatch_base_lane_is_wlh(dq, tq)) {
// 如果 dq 和 tq 共享相同的 WLH,设置 role 为 BASE_WLH
role = DISPATCH_QUEUE_ROLE_BASE_WLH;
} else {
// 否则,将 role 设置为 BASE_ANON
role = DISPATCH_QUEUE_ROLE_BASE_ANON;
}

// 使用原子操作更新 dq 的状态,确保线程安全
os_atomic_rmw_loop2o(dq, dq_state, old_state, new_state, relaxed, {
// 清除旧的 role 位
new_state = old_state & ~DISPATCH_QUEUE_ROLE_MASK;
// 设置新的 role 位
new_state |= role;
// 如果状态没有改变,则退出循环
if (old_state == new_state) {
os_atomic_rmw_loop_give_up(break);
}
});

// 如果旧状态是 base WLH 且新状态不是
if (_dq_state_is_base_wlh(old_state) && !_dq_state_is_base_wlh(new_state)) {
dispatch_deferred_items_t ddi = _dispatch_deferred_items_get();
if (ddi && ddi->ddi_wlh == (dispatch_wlh_t)dq) {
_dispatch_event_loop_leave_immediate(new_state);
}
}

// 再次检查目标队列 tq 是否具有 QUEUE_ROOT 类型标志
if (!dx_hastypeflag(tq, QUEUE_ROOT)) {
// 定义需要清除和设置的标志
dispatch_queue_flags_t clear = 0, set = DQF_TARGETED;
// 如果 tq 类型是 WORKLOOP_TYPE
if (dx_metatype(tq) == _DISPATCH_WORKLOOP_TYPE) {
clear |= DQF_MUTABLE;
#if !DISPATCH_ALLOW_NON_LEAF_RETARGET
} else {
clear |= DQF_MUTABLE;
#endif
}
// 如果有需要清除的标志,则进行清除和设置操作
if (clear) {
_dispatch_queue_atomic_flags_set_and_clear(tq, set, clear);
} else {
_dispatch_queue_atomic_flags_set(tq, set);
}
}
}

这里主要关注角色位 role 配置,role 最终会参与 dq_state 的赋值 new_state |= role;

role 赋值判断逻辑如下:

1
2
3
4
5
6
7
if (!dx_hastypeflag(tq, QUEUE_ROOT)) {
role = DISPATCH_QUEUE_ROLE_INNER;
} else if (_dispatch_base_lane_is_wlh(dq, tq)) {
role = DISPATCH_QUEUE_ROLE_BASE_WLH;
} else {
role = DISPATCH_QUEUE_ROLE_BASE_ANON;
}

dx_hastypeflag 是个宏,根据其源码定义以及 _dispatch_queue_concurrent_vtable_dispatch_queue_serial_vtable 可知,无论串行队列还是并发队列,dx_hastypeflag(tq, QUEUE_ROOT) 都是 true,所以不会走第一个 if 分支,故会进入 _dispatch_base_lane_is_wlh 函数的判断逻辑。

_dispatch_base_lane_is_wlh 函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 检查给定的调度通道是否是工作循环(workloop)的一部分
static inline bool
_dispatch_base_lane_is_wlh(dispatch_lane_t dq, dispatch_queue_t tq)
{
// 如果工作队列事件机制未启用,则返回 false
if (unlikely(!_dispatch_kevent_workqueue_enabled)) {
return false;
}

// 检查调度通道的类型是否为网络事件队列类型
if (dx_type(dq) == DISPATCH_QUEUE_NETWORK_EVENT_TYPE) {
// 如果是网络事件类型,则返回 true
return true;
}

// 检查调度通道的元类型是否为调度源类型
if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {
// 源类型不支持同步等待者,因此那些不会改变 QoS 的源
// 无法受益于任何具有开销的工作循环特性,
// 因此只使用工作队列的 kqueue。
if (likely(!upcast(dq)._ds->ds_refs->du_can_be_wlh)) {
// 如果调度源不可以是工作循环,则返回 false
return false;
}
// 确保直接引用
dispatch_assert(upcast(dq)._ds->ds_refs->du_is_direct);
}

// 检查队列宽度是否为 1 且目标队列在根队列数组中
return dq->dq_width == 1 && _dispatch_is_in_root_queues_array(tq);
}

static inline bool
_dispatch_is_in_root_queues_array(dispatch_queue_class_t dqu)
{
/**
* 判断调度队列的指针范围是否在根队列数组的有效范围内
*
* _DISPATCH_ROOT_QUEUE_IDX_COUNT = 18
*
* 根据前面 "创建 tq" 部分,创建串行队列时,index = 10,创建并发队列时 index = 9,均满足 >= 0 && < 18
*/
return (dqu._dgq >= _dispatch_root_queues) &&
(dqu._dgq < _dispatch_root_queues + _DISPATCH_ROOT_QUEUE_IDX_COUNT);
}

上述逻辑中,无论串行队列还是并发队列,_dispatch_is_in_root_queues_array 函数都是返回 true。所以,最终 _dispatch_base_lane_is_wlh 函数返回值取决于 dq->dq_width == 1 的判断结果,即:

  • 创建串行队列时,返回值为 true
    • role = DISPATCH_QUEUE_ROLE_BASE_WLH;
    • dq_state = (dq_state | role) = (DISPATCH_QUEUE_STATE_INIT_VALUE(dq_width) | DISPATCH_QUEUE_ROLE_BASE_WLH);
  • 创建并发队列时,返回值为 false
    • role = DISPATCH_QUEUE_ROLE_BASE_ANON;
    • dq_state = (dq_state | role) = (DISPATCH_QUEUE_STATE_INIT_VALUE(dq_width) | DISPATCH_QUEUE_ROLE_BASE_ANON);

(5)简单总结

总结一下:

  • 串行队列

    • dq_width = 1;
      • 即串行队列最多能同时处理 1 个任务
    • dq_state = (DISPATCH_QUEUE_STATE_INIT_VALUE(dq_width) | DISPATCH_QUEUE_ROLE_BASE_WLH);
    • 串行队列是 overcommit
  • 并发队列

    • dq_width = 4094
      • 即串行队列最多能同时处理 4094 个任务,但最终有多少个任务能够同时执行,还要看系统资源情况,例如线程池可用线程数量等。
    • dq_state = (DISPATCH_QUEUE_STATE_INIT_VALUE(dq_width) | DISPATCH_QUEUE_ROLE_BASE_ANON);
    • 并发队列是非 overcommit

对于 do_targetq,串行队列、并发队列的 do_targetq,都是从根队列数组 _dispatch_root_queues 中取出的对应的根队列(root queue),只不过两者取的 index 不一样。

同时,根据源码中宏定义:

1
2
3
4
5
6
7
8
#define DISPATCH_QUEUE_STATE_INIT_VALUE(width) \
((DISPATCH_QUEUE_WIDTH_FULL - (width)) << DISPATCH_QUEUE_WIDTH_SHIFT)

#define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull
#define DISPATCH_QUEUE_WIDTH_SHIFT 41

#define DISPATCH_QUEUE_ROLE_BASE_WLH 0x0000002000000000ull
#define DISPATCH_QUEUE_ROLE_BASE_ANON 0x0000001000000000ull

可以计算出:
串行队列:

1
dq_state = (DISPATCH_QUEUE_STATE_INIT_VALUE(dq_width) | DISPATCH_QUEUE_ROLE_BASE_WLH)  = (DISPATCH_QUEUE_STATE_INIT_VALUE(1) | DISPATCH_QUEUE_ROLE_BASE_WLH) = 0x001ffe2000000000

并发队列:

1
dq_state = (DISPATCH_QUEUE_STATE_INIT_VALUE(4094) | DISPATCH_QUEUE_ROLE_BASE_ANON) = 0x0000041000000000

关于 dq_widthdq_state,我们可以使用一个 Demo 验证一下:

1
2
3
4
5
6
7
// 串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.lixkit.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

NSLog(@"serialQueue = %@", [serialQueue debugDescription]);
NSLog(@"concurrentQueue = %@", [concurrentQueue debugDescription]);

运行后打印结果:

上述打印结果中,width 就是 dq_width

  • 串行队列 width = 0x1,转成十进制就是 1
  • 并发队列 width = 0xffe,转成十进制就是 4094

打印结果的 state 就是 dq_state,可以看到打印出的 dq_widthdq_state 和前面我们根据源码计算出的结果一致。

这里为什么要重点关注 dq_widthdq_statedo_targetq,因为后续 dispatch_syncdispatch_async 源码中会用到。

Tags: 底层