GCD 之 函数与队列
GCD全称 总结: GCD中有两种执行任务的方式: 具有 只有在 特殊的 专门用来 GCD默认的 第一个参数表示 第二个参数使用0 在日常开发中, 【任务按顺序执行】:任务一个接一个的在当前线程执行,不会开辟新线程 【任务按顺序执行】:任务一个接一个的执行,会开辟新线程 【任务按顺序执行】:任务一个接一个的执行,不开辟线程 【任务乱序执行】:任务执行无顺序,会开辟新线程 【造成死锁】:任务相互等待,造成死锁 死锁现象 主线程因为你同步函数的原因等着先执行任务 主队列等着主线程的任务执行完毕再执行自己的任务 主队列和主线程相互等待会造成死锁 【任务按顺序执行】:任务一个接一个的执行,不开辟线程 【任务按顺序执行】:任务一个接一个的执行,不开辟新线程 【任务乱序执行】:任务乱序执行,会开辟新线程 主队列的任务是 在 在 任务1和任务5的分析和前面面试一样,所以执行顺序是 在异步block中首先执行任务2,因为同步函数会 在 开始执行时,先执行 因为同步函数会 下面代码的执行顺序是什么?(答案是 AC) 因为 同理, 因为 队列只有两种: 并发队列: 串行队列:Grand Central Dispatch
,基于C语言的函数,会自动
利用更多的CPU内核
,自动管理线程生命周期
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)
并发队列
dispatch_get_global_queue(intptr_t identifier, uintptr_t flags);
获取全局并发队列队列优先级
,默认DISPATCH_QUEUE_PRIORITY_DEFAULT=0
,在iOS9.0后被服务质量quality-of-service
取代//全局并发队列的获取方法
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、异步block
阻塞线程
,所以执行顺序是任务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】异步函数+同步函数+并发队列
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)
作者:Study_Min
链接:https://juejin.cn/post/7016617855751815182