一:GCD 线程保活功能
- GCD 没有线程保活功能。
- 线程保活功能 只能通过 runloop 来执行。
- GCD 只是在block 中执行代码,在block 中开启runloop。
- GCD 和 runloop 不是同一回事。
- GCD 只负责开启线程,然后去执行任务。不负责线程保活。
- 就算 GCD 的线程活下来了,也是因为 GCD里面用了 runloop。
使用sync 函数 往当前 串行队列中添加任务,会卡住当前的串行队列(产生死锁)
二:主队列 + 同步任务 = 死锁
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"执行任务1");
[self GCDDeadlock];
NSLog(@"执行任务3");
}
- (void)GCDDeadlock {
// 问题: 以下代码是在主线程执行的,会不会产生死锁?
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2");
})
}
执行结果:
- 崩溃信息:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
- 主队列 + 同步任务 = 死锁
第一种解释:
- dispatch_get_main_queue() : 添加到主队列,代表要添加到主线程执行任务
- dispatch_sync 的block中的代码是要添加到主队列,意味着: 要添加到主线程中去执行
- 而dispatch_sync是同步任务, 意味着:要在当前线程去执行。
- 在当前线程中执行,又是主队列。那就意味着,block 中的内容要放在主线程中执行
- 可能会认为 这不就直接 1, 2, 3 的执行了么? 为什么会死锁。
- 因为 queue 是队列,队列的一个基本含义就是:排队,FIFO(先进先出First In First Out). 哪个任务先进来,就先执行哪个任务。
- 首先,将 block 中的任务 放到 main_queue(主队列)中,将来主队列 需要取出里面的任务来执行。
- 但实际上主线程已经有ViewDidLoad 这个任务了。 ViewDidLoad 方法的执行就是一个完整的任务
- 如果想要从 queue 中取出 block 中的任务的话,需要等 上一个(ViewDidLoad)任务执行完毕,才能执行这个任务。
- 由于 block 任务是 dispatch_sync (马上在当前线程中执行任务),也就意味着,我希望马上要执行完block 里面的东西。执行完 block 中的内容后,才能执行下边的任务(NSLog(@“执行任务3”);这句代码)。
结论:
- 要想执行 block中的内容,需要 先把 ”执行任务3“ 执行完毕,但是要想执行 ”执行任务3“ 就得把 dispatch_sync 执行完毕。
- 任务2 等任务3 执行完毕;
- 任务3 等任务2 执行完毕;
- 最终导致死锁。
第二种解释:
- queue 是主队列,会在主线程执行任务
- dispatch_sync 是同步任务: 在当前线程中执行任务,不具备开启新线程的能力
- 主队列 + 同步任务:没有开启新线程,串行执行任务
- 串行执行任务:一个任务执行完毕后,再执行下一个任务。
- ViewDidLoad 方法 是一个任务
- dispatch_sync 的 block 也是一个任务
- 从上到下执行任务,按理说应该是 “任务1”, “任务2” ,“任务3”。 但是 dispatch_sync 是同步扔一个 block 到 queue 中,扔了 我就等着。等到 queue 排队把这个block 执行完了之后,才继续执行下一行代码。
- 比如 viewDidLoad 在书线程 ,排队为 A;
- dispatch_sync 也在主线程,排队为B;
- A 在执行一半的时候,需要等到自己的下一个任务B 执行完,自己才能继续执行。
- 而 B 排队在后面,需要等A 执行完毕才能执行。
- 这时死锁就产生了。 A 和 B 互相等待执行,当前线程就卡死在 dispatch_sync 这行代码处。
三、主队列 + 异步任务 = 不死锁
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"执行任务2");
});
NSLog(@"执行任务3");
}
打印结果:
- dispatch_async 不要求立马在当前线程同步执行任务
四、 会不会产生死锁
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"执行任务2");
dispatch_sync(queue, ^{
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}
执行结果:会产生死锁
- 在 dispatch_sync 这行会产生死锁
五、两个不一样的队列,会不会产生死锁
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
// 创建并发队列
dispatch_queue_t queue1 = dispatch_queue_create("WYqueue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"执行任务2");
dispatch_sync(queue1, ^{
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}
不会产生死锁。
执行顺序是:
六、两个不同的串行队列,会不会死锁
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("WYqueue1", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"执行任务2");
dispatch_sync(queue1, ^{
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}
不会死锁。
执行顺序
七、两个任务用一个并发队列
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"执行任务2");
dispatch_sync(queue, ^{
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}
不会产生死锁
并发队列:可以执行多个任务。
执行结果
八、全局队列 、并发队列
- 全局队列:不管创建几次,地址都一样;
- 并发队列:不管创建几次,地址都不一样。
打印地址结果: