GCD全称Grand Central Dispatch
,基于C语言的函数,会自动
利用更多的CPU内核
,自动管理线程生命周期
,
总结:GCD就是将任务添加到队列,并指定任务执行的函数
函数
GCD中有两种执行任务的方式:同步函数(dispatch_sync)
和异步函数(dispatch_async)
同步函数(dispatch_sync)
-
必须等待
当前语句执行完毕,才会执行下一条语句,会阻塞当前线程 不会开辟新的线程
异步函数(dispatch_async)
-
不必等待
当前语句执行完毕,就可以执行下一条语句 - 具有
开辟新线程的能力
,但不一定会开辟新线程,与当前任务所指定的队列类型相关
队列
队列(dispatch queue)
是一种数据结构,特殊的线性表
,是用来存放任务的队列,遵循FIFO(先进先出)原则
,新任务总是被插入到队尾
,任务从队首开始读取
,每读取一个任务,该任务就会从队列总释放,
串行队列
同一时刻只能执行一个任务
-
dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);
创建串行队列 -
DISPATCH_QUEUE_SERIAL
也可以用NULL
代替
// 串行队列的获取方法
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.CJL.Queue", NULL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_SERIAL);
串行队列
并发队列
同一时刻可以执行多个任务
-
dispatch_queue_create("xxx", DISPATCH_QUEUE_CONCURRENT);
创建并发队列 - 只有在
异步函数
下才有并发效果
// 并发队列的获取方法
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
并发队列
主队列
主队列(Main Dispatch Queue)
- 特殊的
串行队列
- 专门用来
在主线程上调度任务的串行队列
,依赖于主程序、主Runloop,在main函数
之前调用 -
dispatch_get_main_queue()
获取主队列
//主队列的获取方法
dispatch_queue_t mainQueue = dispatch_get_main_queue();
全局队列
全局队列(Global Dispatch Queue)
- GCD默认的
并发队列
-
dispatch_get_global_queue(intptr_t identifier, uintptr_t flags);
获取全局并发队列- 第一个参数表示
队列优先级
,默认DISPATCH_QUEUE_PRIORITY_DEFAULT=0
,在iOS9.0后被服务质量quality-of-service
取代 - 第二个参数使用0
- 第一个参数表示
//全局并发队列的获取方法
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
//优先级从高到低(对应的服务质量)依次为
- DISPATCH_QUEUE_PRIORITY_HIGH -- QOS_CLASS_USER_INITIATED
- DISPATCH_QUEUE_PRIORITY_DEFAULT -- QOS_CLASS_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW -- QOS_CLASS_UTILITY
- DISPATCH_QUEUE_PRIORITY_BACKGROUND -- QOS_CLASS_BACKGROUND
在日常开发中,全局队列+并发并列
一般是这样配合使用的
//主队列 + 全局并发队列的日常使用
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//执行耗时操作
dispatch_async(dispatch_get_main_queue(), ^{
//回到主线程进行UI操作
});
});
函数与队列的不同组合
串行队列 + 同步函数
不会开辟线程
,任务一个一个按顺序执行
串行队列 + 异步函数
开辟线程
,任务一个一个按顺序执行
并行队列 + 同步函数
不开辟新线程
,任务一个一个按顺序执行
并行队列 + 异步函数
开辟新线程
,任务一起乱序执行
总结
队列+函数相关面试题
【面试题1】异步函数+并发队列
- (void)interview01{
//并行队列
dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
// 耗时
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
----------打印结果-----------
输出顺序为:1 5 2 4 3
异步函数
会开辟新线程,不会阻塞
主队列,
分析
- 主队列的任务是
NSLog(1)、异步Block、NSLog(5)
,因为NSLog(1)
和NSLog(5)
的复杂度是一样的,而异步Block
的复杂度更高,所以NSLog(1)和NSLog(5)
优先于异步Block
- 在
异步Block
中同理,NSLog(2)和NSLog(4)
优先于异步Block
, - 在
主线程阻塞
或其他极端情况下,NSLog(2)
有可能优先NSLog(1)
和NSLog(5)
【面试题2】异步函数嵌套 同步函数+并发队列
- (void)interview02{
//并发队列
dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
//异步函数
dispatch_async(queue, ^{
NSLog(@"2");
//同步函数
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
----------打印结果-----------
输出顺序为:1 5 2 3 4
分析
- 任务1和任务5的分析和前面面试一样,所以执行顺序是
任务1、任务5、异步block
- 在异步block中首先执行任务2,因为同步函数会
阻塞线程
,所以执行顺序是任务2、任务3、任务4
【面试题3】异步串行嵌套同步串行
- (void)interview03{
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", NULL);
NSLog(@"1");
// 异步函数
dispatch_async(queue, ^{
NSLog(@"2");
// 同步函数
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
----------打印结果-----------
输出顺序为:1 5 2 死锁崩溃
分析
-
任务1、任务5、异步block
的执行顺序和上面分析的一样 - 在
异步block
中,首先将任务2
、同步block
、任务4
添加到串行队列,等待执行,串行队列中的任务顺序任务2 --> 同步block --> 任务4
, - 开始执行时,先执行
任务2
,再执行同步Block
,因为是同一个串行队列,所以会将任务3
添加在任务4
的后面,串行队列中的任务顺序同步block --> 任务4 -->任务3
, - 因为同步函数会
阻塞当前线程
,所以任务4
等待同步block
执行完毕,但是当前又是串行队列
,遵循先进先出原则,所以任务3
等待任务4
,造成死锁
-
死锁
会有一个关键的堆栈信息_dispatch_sync_f_slow
【面试4】异步函数+同步函数+并发队列
下面代码的执行顺序是什么?(答案是 AC)
A: 1230789
B: 1237890
C: 3120798
D: 2137890
- (void)interview04{
//并发队列
dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", 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");
});
}
----------打印结果-----------
输出顺序为:(1 2 3 无序)0(7 8 9 无序),可以确定的是 0 一定在3之后,在789之前
分析
- 因为
任务1、任务2
是异步并发,会开辟新线程,所以没有固定顺序, - 同理,
任务7、任务8、任务9
也没有固定顺序 - 因为
任务3
是同步并发,会阻塞当前线程,所以任务3
在任务0
之前执行,所以任务0
会在任务3
之后,任务7、8、9
之前
【面试题5】下面队列有几种类型
/串行队列 - Serial Dispatch Queue
dispatch_queue_t serialQueue = dispatch_queue_create("com.CJL.Queue", NULL);
//并发队列 - Concurrent Dispatch Queue
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
//主队列 - Main Dispatch Queue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//全局并发队列 - Global Dispatch Queue
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
队列只有两种:
- 并发队列:
并发队列(DISPATCH_QUEUE_CONCURRENT)
、全局并发队列(dispatch_get_global_queue)
- 串行队列:
串行队列(DISPATCH_QUEUE_SERIAL \ NULL)
、串行主队列(dispatch_get_main_queue)
网友评论