iOS开发中,无法避免都要使用到GCD(Grand Central Dispatch)
,我们只需要把待执行的任务放到适合的Dispatch Queue
中,GCD
就能帮我们把任务放进合适的线程中执行,而我们并不用管理和操作线程的生命周期,使用起来非常方效率高发票查验便。本文通过面试题由浅入深一探开发常用GCD的源码实现。
经典面试题
看面试题之前我们先复习两个单词:
单词 | 解释 |
---|---|
se初始化电脑时出现问题rial | 顺序源码中的图片排列的,连续的;连续播放的,初始化英文连载的;串行的 |
concurrent | 并存的,同时发生的 |
题目一
以下代码输出结果是什么?
-(void)interview01 {
dispatch_queue_t queue = dispatch_queue_create("com.demo.queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"test____1"); // 任务1
dispatch_async(queue, ^{
NSLog(@"test____2"); // 任务2
dispatch_sync(queue, ^{
NSLog(@"test____3"); // 任务3
});
NSLog(@"test____4"); // 任务4
});
NSLog(@"test____5"); // 任务5
}
先运行一下,看真实的输出:
可以看初始化电脑的后果到输出结果为:1⃣️5⃣️2⃣️3⃣️4⃣️。 如果你正好回答到这个答案,那么恭喜你,你答对了。但回答的并不完美。我们先来分析系统输出的答案。
首先这段代码是在主线程执行的,那么在主线程执行的内容我们可以分为3个部分:
任务1⃣️肯定是最先执行的,然后去执行第2块内容,最后执行任务5⃣️,再继续看第2块使用异步函数配iOS合并发队列,那么disptch里面的block将会在一个新的线程执行。同样源码网站的在这效率集个线程里面也可以将执线程是什么意思行的代码分成3个部分:
在这块代码里面任务2⃣️会被先执行,然后再看dispatch_syns
,同步函数,那么都不需要看是什么对列,任务3⃣️将会次之执行,最后就是执线程数是什么行任务4⃣️。
剩下的就是需要考虑任务5⃣️和任务2⃣️3⃣️4⃣️的执行顺序了。正确答案其实是他们之间没有顺线程池面试题序,他们在不同的线程执行,理论上讲是可以同时执行的。而之所以打印出来任务2⃣️3⃣️4⃣️在任务5⃣️之后,其实是在执行任务2⃣️3⃣️4⃣️之前,需要创建其执行的线程,创建线程需要消耗微小的时间,还有一方面ios模拟器原因是这块代码在主线程运行的,任务5⃣️在主线程线程池的七个参数执行,效率也会更高。
这道效率集题考查的是G源码编程器CD中同步、异步函数和串行、并行队列的使用,使用NSLog
来模拟任务执行,但正真的环境中任务的耗时程度是不一样的。
我们假设任务5⃣️是一个耗时20微秒的任务,那么打印结果会不会不一样呢?
在任务5⃣️之前usleep
20微秒模拟耗源码中的图片时任务。
注意:uslios系统eep
和sleep
的区别只是单位不一样,usleep
的单位是微秒,sleep
的单位是秒。
可以看到这时候顺序变成了1⃣️2⃣️5⃣️3⃣️4⃣️,正如我们分析结果,5⃣️和2⃣️3⃣️4⃣️是没有顺序的。如果200微妙,运行结果还会是不一样的。
所以这道题面试题的正确答案线程池是:先执行1⃣️,5⃣️在1⃣️后执行,2⃣️3⃣效率高发票查验️4⃣️顺序执行,5⃣️和2⃣️3⃣️4⃣️是没有顺序。
题目二
以下代码输出结果是什么?
-(void)interview01 {
dispatch_queue_t queue = dispatch_queue_create("com.demo.queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"test____1"); // 任务1
dispatch_async(queue, ^{
NSLog(@"test____2"); // 任务2
dispatch_sync(queue, ^{
NSLog(@"test____3"); // 任务3
});
NSLog(@"test____4"); // 任务4
});
NSLog(@"test____5"); // 任务5
}
相对于题目一,这题就没那么老六了。
直接上结果:
可以看到是崩溃,奔溃原源码编辑器下载因很简单初始化磁盘:使用同步函数
在当前
串行队列中
添加任务,会产生死锁。注意三个关键字,GCD发生死锁的充分iOS条件。
同步异步和串行并发队列的搭配
同步异步和效率集串行并发队列的组合情况如下:
串行ios越狱队列 | 并发队列 | |
---|---|---|
同步函数 | 可能死锁 | 不开启新线程 |
异步函数 | 开源码编程器启新线程 | 开启新线程 |
在组合的时候,还效率意识方面存在的问题需要考虑的因素还有当前代码的执行是在哪个队列之中,之前也有讲到:使用同步函数
在当前
串行队列中
添加任务,会产生死锁,如果在别的串行队列去添加任务是不会产生死锁的。例如:
- (void)interview02 {
dispatch_queue_t queue = dispatch_queue_create("com.demo.serial", DISPATCH_QUEUE_SERIAL);
NSLog(@"---任务1");
dispatch_sync(queue, ^{
NSLog(@"---任务2");
});
NSLog(@"---任务3");
}
这段代码就是同步函数和串行队列的时候,但不会造成死锁(在主线程中线程是什么意思执行)。
串行队列和并发ios是什么意思队列的源码解析
在我们开发中,使用队列的时候,苹果给我们给了3个获取队列的api。
- (void)test01 {
//主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//全局并发队列
dispatch_queue_t globQueue = dispatch_get_global_queue(0, 0);
//自己创建的串行队列
dispatch_queue_t normalQueue = dispatch_queue_create("com.demo.serial", DISPATCH_QUEUE_SERIAL);
NSLog(@"%@",mainQueue);
NSLog(@"%@",globQueue);
NSLog(@"%@",normalQueue);
}
我们打开效率高发票查验dispatch源码找到dispatch_queue_cre源码时代ate
的api实现部分,来一探究竟。
可以看到是调用_dispatch_lane_create_with_target
并添加2个默效率集认参数实现的,找到对应实现。
可以看到这个方法里面,把我们传入的DISPATCH_QUEUE_SERIAL
或者DISPATCH_QUEUE_CONCURRENT
参数进行封装,封装成了dqai
。我们可以大致看看封装的实现:
注意看这里我们可以获取2个有用的点,dqaios是什么意思i
里面有个dqai_c线程池的七个参数oncurrent
的属性,顾名思义是代表是否是并发,那iOS么默认的就是串行。
我们在继续看如何根据dqai
创建队列的。
可以看到通过init
方法初始化,第三个参数,如果是并发传入DISPATCH_QUEUE_WIDTH_MAX
,如果是串行传入1
而这里是DISPios16ATCH_QUEUEios应用商店_WIDTH效率集_MAX
的定义,可以计算其初始化英文结果是14
。
我们再看init
函数内部实现源码1688
可以看到如果width
最后变成了DQF_Wios16IDTH(width)
。
接下来我们看看主队列的实现:
可以看到需要用到一个宏ios模拟器定义的函数,并且传入了2个参数,其中_dispatch_main_q
是源码精灵永久兑换码全局变量,定义如下:
这里我们再次看到了DQF_WIDTH(1)
根源码编辑器下载据源码里面的信息,我们可以得知,串行队列和并发队列最根本的区别就是DQF_WIDTH
不同,串行队列的为1
。这个width
可以抽象的理解为队列出口的宽度。可以把串行ios是什么意思队列想成一个单向线程单源码之家车道,把任务想成一辆辆车子,车子通过的时候必须一辆一辆按顺序通过;而并发队列可以想成单向多车道,有多个出口,车子可以并行通过。
当然也可以通过下ios15图理解:
参线程考链接
iOS GCD源码浅析
iOS底层原理(七)多线程(上