GCD 初探
什么是GCD?
GCD 全称是 Grand Central Dispatch, 纯 C 语言,提供了非常多强大的函数
GCD 的优势
- GCD 是苹果公司为多核的并行运算提出的解决方案
- GCD 会自动利用更多的 CPU 内核(比如双核,四核)
- GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码
函数
使用例子
- (void)syncTest{
// 任务 队列 函数
dispatch_block_t block = ^{
NSLog(@"hello word");
};
dispatch_queue_t queue = dispatch_queue_create("com.janice.cn", NULL);
dispatch_async(queue, block);
}
以上便是将任务添加到队列,并且指定执行任务的函数
详解:
- 任务使用 block 封装,任务的 block 没有参数也没有返回值;
- 执行任务的函数
- 异步 'dispatch_async'
- 不用等待当前语句执行完毕,就可以执行下一条语句
- 会开启线程执行 block 的任务
- 异步是多线程的代名词
- 同步 'dispatch_sync'
- 必须等待当前语句执行完毕,才会执行下一条语句
- 不会开启线程
- 在当前执行 block 的任务
队列 (FIFO)
串行队列
一次只能执行一个,上一个任务没有执行完,就无法继续执行下一个任务,也就是效率比较低,任务耗时较长,DISPATCH_QUEUE_SERIAL。
并行队列
并发队列会开启多个线程来执行任务,所以可以同时执行多个任务,任务执行的顺序也不会固定,DISPATCH_QUEUE_CONCURRENT。队列和函数
- 同步函数串行队列
- 不会开启线程,在当前线程执行任务
- 任务串行执行,任务一个接着一个
- 会产生堵塞
- 同步函数并发队列
- 不会开启线程,在当前线程执行任务
- 任务一个接着一个
- 异步函数串行队列
- 开启线程一条新线程
- 任务一个接着一个
- 异步函数并发队列
- 开启线程,在当前线程执行任务
- 任务异步执行,没有顺序,CPU 调度有关
执行顺序练习
练习一
- (void)textDemo{
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
执行顺序:1,5,2,4,3
练习二
- (void)wbinterDemo{
dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
// 堵塞 - 护犊子
dispatch_sync(queue, ^{
NSLog(@"3");
});
// **********************
NSLog(@"0");
dispatch_async(queue, ^{
NSLog(@"7");
});
dispatch_async(queue, ^{
NSLog(@"8");
});
dispatch_async(queue, ^{
NSLog(@"9");
});
}
A: 1230789 B: 1237890 C: 3120798 D: 2137890
正确答案:A、C
练习三
- (void)textDemo2{
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
// 异步函数
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
中间执行任务队列示意图:
说明:
- 按照代码顺序,打印 2 任务拍照在队列的第一位
- 然后就是同步函数任务块
- 然后就是打印 4 任务
- 然后调步到同步函数任务块是,发现里面还有个打印 3 任务,按照队列的 FIFO 特性,于是就在打印 4 任务的后面。
执行流程
- 打印 2 ;
- 执行同步函数。
- 因为打印 4 任务之前,是个同步函数,所以就会堵塞,也就是说前面的同步函数不执行完,打印 4 任务就不会被执行
- 同步函数里面的打印 3 任务,又要等前面的打印 4 任务执行了才能被执行
- 因此这个流程机会形成一个死锁,互相等待。打印 4 任务等待前面的同步函数块执行完;同步函数里面的打印 3 任务又在等待打印 4 任务执行完。所以永远都是等待,无法往下执行。
练习四
- (void)textDemo3{
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
// 异步函数
dispatch_async(queue, ^{
NSLog(@"2");
NSLog(@"4");
dispatch_sync(queue, ^{
NSLog(@"3");
});
});
NSLog(@"5");
}
打印结果:
练习四在练习三的基础上做了一丢丢改变,将打印 4 任务放在同步函数块的前面,再来分析一下
中间执行任务队列示意图:
说明:
- 按照代码顺序,打印 2 任务拍照在队列的第一位
- 然后就是打印 4 任务
- 然后就是同步函数任务块
- 然后调步到同步函数任务块是,发现里面还有个打印 3 任务,按照队列的 FIFO 特性,于是就在同步函数任务块的后面。
执行流程
- 打印 2 ;
- 打印 4 ;
- 执行同步函数块任务,函数块执行完就意味着要等打印 3 任务执行完,才算结束。
- 因为打印 3 任务之前,是个同步函数块,所以就会堵塞,也就是说前面的同步函数块不执行完,打印 3 任务就不会被执行;
- 因此这个流程机会形成一个死锁,互相等待。打印 3 任务等待前面的同步函数块执行完;同步函数里面的打印 3 任务又在等待同步函数块执行完。所以永远都是等待,无法往下执行。
打印结果:
**总结:**练习三、练习四 ,如果异步函数里面嵌同步函数,就会发生死锁!!!。
死锁
- 主线程因为同步函数的原因等着先执行任务
- 主队列等着主线程的任务执行完毕再执行自己的任务
- 主队列和主线程相互等待会造成死锁
队列创建原理
dispatch_queue_create 是如何创建的?
就需要去探索队列创建的源码
获取地址:链接:pan.baidu.com/s/1Avpzm947… 提取码:l8ci
创建队列过程源码分析
- 首先找到 dispatch_queue_create 入口
- 之前我们在实际创建的队列的时候,想要串行队列或并发队列是对接口的第二个参数进行设置,DISPATCH_QUEUE_SERIAL(串行)、DISPATCH_QUEUE_CONCURRENT(并行),如下
- DISPATCH_QUEUE_SERIAL 的定义
- DISPATCH_QUEUE_CONCURRENT 的定义
所以在探索源码的时候我们也看下源码是怎么区分的
_dispatch_lane_create_with_target 源码
DISPATCH_NOINLINE
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
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 (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");
}
}
if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
// Handle discrepancies between attr and target queue, attributes win
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;
}
}
if (qos == DISPATCH_QOS_UNSPECIFIED) {
qos = _dispatch_priority_qos(tq->dq_priority);
}
tq = NULL;
} else if (tq && !tq->do_targetq) {
// target is a pthread or runloop root queue, setting QoS or overcommit
// is disallowed
if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
"and use this kind of target queue");
}
} else {
if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
// Serial queues default to overcommit!
overcommit = dqai.dqai_concurrent ?
_dispatch_queue_attr_overcommit_disabled :
_dispatch_queue_attr_overcommit_enabled;
}
}
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");
}
}
//
// 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) {
// 通过dqai.dqai_concurrent 来区分并发和串行
// OS_dispatch_queue_concurrent_class
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;
}
}
// 开辟内存 - 生成响应的对象 queue
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 源码
外面传进来的队列类型参数为dpa,于是看下这段代码
看到有个判断,如果 dpa 为空,就直接返回 dqai,前面有贴出,当 串行队列 DISPATCH_QUEUE_SERIAL 的时候的定义就是为空 ,否则做其他处理,返回
- 串行和并行创建的地方
创建关键步骤:
-
第一步初始化并分配内存空间
-
构造方法
1)第一个参数 dq 对象
2)dqai.dqai_concurrent 第三参数,如果为并发队列便标识,传 DISPATCH_QUEUE_WIDTH_MAX,否则 1.
- 设置标签
- 设置优先级
- overcommit:串行为1 ,并发为 2。
overcommit 的赋值
- 设置target
dq->do_targetq = tq;
- tq 的创建
根队列分析
打印获取到的主队列和全局并发队列,因为是全局静态结构体
主队列结构体
全局队列属性打印
通过打印可知,全局变量的 width = 0xfff,上面有打印并发队列的 width = 0xfffe
队列结构体
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,
),
};
出来 main queue 其他指针都是由 root queue 模板创建的
dispatch_object_t
typedef struct dispatch_object_s {
private:
dispatch_object_s();
~dispatch_object_s();
dispatch_object_s(const dispatch_object_s &);
void operator=(const dispatch_object_s &);
} *dispatch_object_t;