本篇主要记录三个问题
1、GCD面试题分享
2、GCD相关的死锁
3、串行并发底层分析
1、GCD面试题分享
首先我们说一下什么是GCD
- GCD的全称是Grand Central Diapatch,是纯C语言编写的,提供了跟多强大的函数
- GCD的优势:
GCD是Apple公司提出的多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核
会自动管理线程的生命周期(创建线程、调度任务、销毁线程) - 任务使用block封装
- 任务的block没有参数 也没有返回值
- 执行任务的函数
*异步 dispatch_async
不用等待当前语句执行完成 即可执行下条任务
会开启线程执行block任务
异步是多线程的代名词
*同步 dispatch_sync
必须等待当前任务完成 才会执行下一条任务
不会开启线程
在当前block中执行任务 - 队列
*串行队列,先进先出,顺序执行 DQF_WIDTH = 1
*并发队列 ,多个任务同时执行 DQF_WIDTH = MAX,任务完成时间不确定 要看任务的复杂度 - 线程和队列 可以认为没有直接的关系,但是线程和队列同处于同一个进程中,会相互影响,同时同一个任务执行的快慢 与线程和队列都有关系
线程的状态
CPU的调度
队列的优先级
任务的复杂度,都会影响任务的执行 - 串行队列
dispatch_queue_create("queue.name", DISPATCH_QUEUE_SERIAL);
所有任务按顺序依次执行,结束顺序固定,符合先进先出的基本原则,队列后面的任务必须等待前面的任务执行完毕后才出队列。但是,不要认为串行队列中的所有任务都在同一个线程中执行,串行队列中的异步任务,可能会开启新线程去执行。
- 并发队列
dispatch_queue_create("queue.name", DISPATCH_QUEUE_CONCURRENT);
所有任务可以同时执行,结束顺序不固定,只要有可用线程,则队列头部任务将持续出队列。
- 主队列
dispatch_get_main_queue()
本质是一个特殊的串行队列,主队列的任务都在主线程来执行,专门负责调度主线程度的任务,无法开辟新的线程。所以,在主队列下的任务不管是异步任务还是同步任务都不会开辟线程,任务只会在主线程顺序执行。如果在主线程上已经有任务正在执行,主队列会等到主线程空闲后再调度任务。
- 全局队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
本质是一个特殊的并发队列。在后面加入了“服务质量”和“调度优先级” 两个参数
- 串行队列 + 异步,顺序执行,先进先出,可能会开启新的线程
//串行队列
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//异步任务
for (int i=0; i<5; i++) {
dispatch_async(serial, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
NSLog(@"=============== 所有任务执行完毕 =====");
执行结果:
2022-05-17 15:27:35.976433+0800 myDemo[278:9179] =============== 所有任务执行完毕 =====
2022-05-17 15:27:35.976538+0800 myDemo[278:9420] 这是第 0 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:35.976655+0800 myDemo[278:9420] 这是第 1 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:37.978072+0800 myDemo[278:9420] 这是第 2 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:37.978150+0800 myDemo[278:9420] 这是第 3 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:39.987439+0800 myDemo[278:9420] 这是第 4 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
- 串行队列 + 同步,顺序执行,先进先出,不会开启新的线程
//串行队列
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//同步任务
for (int i=0; i<5; i++) {
dispatch_sync(serial, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
执行结果:
2022-05-17 15:14:04.059889+0800 myDemo[252:4951] 这是第 0 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:04.059996+0800 myDemo[252:4951] 这是第 1 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:06.060559+0800 myDemo[252:4951] 这是第 2 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:06.060716+0800 myDemo[252:4951] 这是第 3 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:08.061826+0800 myDemo[252:4951] 这是第 4 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
- 并发队列 + 异步, 同时执行,完成时间不确定,会开启新的线程
//并发队列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//异步任务
for (int i=0; i<5; i++) {
dispatch_async(comp, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
NSLog(@"=============== 所有任务执行完毕 =====");
执行结果:
2022-05-17 15:39:06.309859+0800 myDemo[294:11951] =============== 所有任务执行完毕 =====
2022-05-17 15:39:06.309962+0800 myDemo[294:12131] 这是第 0 个任务;线程 <NSThread: 0x281719380>{number = 5, name = (null)}
2022-05-17 15:39:06.310064+0800 myDemo[294:12131] 这是第 1 个任务;线程 <NSThread: 0x281719380>{number = 5, name = (null)}
2022-05-17 15:39:06.310165+0800 myDemo[294:12134] 这是第 2 个任务;线程 <NSThread: 0x2817d2300>{number = 4, name = (null)}
2022-05-17 15:39:06.310236+0800 myDemo[294:12134] 这是第 3 个任务;线程 <NSThread: 0x2817d2300>{number = 4, name = (null)}
2022-05-17 15:39:06.311765+0800 myDemo[294:12130] 这是第 4 个任务;线程 <NSThread: 0x281719d40>{number = 6, name = (null)}
- 并发队列 + 同步,顺序执行,先进先出,不会开启线程
//并发队列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//同步任务
for (int i=0; i<5; i++) {
dispatch_sync(comp, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
NSLog(@"=============== 所有任务执行完毕 =====");
执行结果:
2022-05-17 15:37:08.804872+0800 myDemo[287:11040] 这是第 0 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:08.804962+0800 myDemo[287:11040] 这是第 1 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:10.805818+0800 myDemo[287:11040] 这是第 2 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:10.806122+0800 myDemo[287:11040] 这是第 3 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:12.807415+0800 myDemo[287:11040] 这是第 4 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:12.807771+0800 myDemo[287:11040] =============== 所有任务执行完毕 =====
- 主队列 + 异步,不会立即执行,需要等待主队列任务执行完成后 才会执行我们添加的异步任务
- 主队列 + 同步 会相互等待 造成死锁
//同步任务+主线程 == 相互等待,死锁
for (int i=0; i<5; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
- 面试题
- (void)gcdasy {
// dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
dispatch_sync(queue, ^{
NSLog(@"5");
});
/// 15234 - 15243,
}
- (void)gcdsy {
NSLog(@"1");
dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"2");
});
NSLog(@"3");
dispatch_sync(queue, ^{
NSLog(@"4");
});
/// 1324
}
- (void)test6 {
__block NSInteger a = 0;
while (a < 10) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
a ++;
NSLog(@"%ld: %@", a, [NSThread currentThread]);
});
}
NSLog(@"=========%ld", a);
/// a的值不确定 但是肯定大于等于10,a打印的次数不确定,可能很大
}
2、GCD相关的死锁
- 串行队列 同步线程
- (void)gcdasy {
dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{// 死锁
NSLog(@"3");
});
NSLog(@"4");
});
dispatch_sync(queue, ^{
NSLog(@"5");
});
}
- 主队列 + 同步 会相互等待 造成死锁
//同步任务+主线程 == 相互等待,死锁
for (int i=0; i<5; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
3、串行并发底层分析
-
主队列可在任何地方获取到 ,主队列是一个串行队列,由于可在任何地方获取到 是一个全局的静态变量
主队列定义
串行的标志性代码是 DQF_WIDTH(1)
-
初始化队列
image.png
初始化的时候会判断串行并发标志位去限制width是多少,串行指定是1,并发是14
image.png
来看看_dispatch_queue_init的队列初始化,我们关注的队列是串行和并发的根本区别就是DQF_WIDTH(width),串行是DQF_WIDTH(1)
网友评论