使用sync/async和queue的各种搭配时,常常有产生死锁崩溃的情况,现简单介绍几种死锁场景。
例一
- (void)viewDidLoad
{
NSLog(@"---1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"---2");
});
NSLog(@"---3");
});
打印了1,并不会打印2和3,会死锁崩溃,因为dispatch_sync是同步,会阻塞当前主线程,等代码块里面内容执行完后再往下继续执行,然而根据dispatch_get_main_queue判断,代码块内的代码又需在主线程中执行,主线程已被阻塞了,所以代码块处于永远等待中。
例二
- (void)viewDidLoad {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"----1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"----2");
});
NSLog(@"---3");
});
NSLog(@"---4");
while (YES) {}
}
2和3永远不会被打印,因为1在异步子线程中执行,所以可能1比4先打印。
这种情况下,因为2需要在主线程中同步执行,当打印了1后,把异步代码放到后台等待获取主线程,先执行4。然而遇到个死循环阻塞了主线程,而2又需要主线程才能执行,所以2处于永远等待获取主线程。
例三
dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
3和4永远不会被打印被锁死,3(dispatch_sync)同步等待dispatch_async这个代码块执行完再执行。而3又被dispatch_sync阻塞,且加入到队列任务尾部,等待队列queue里的任务执行执行完后再执行。
在看一个正常的场景:
NSLog(@"1"); // 任务1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2"); // 任务2
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3"); // 任务3
});
NSLog(@"4"); // 任务4
});
NSLog(@"5");
分析:
首先,将【任务1、异步线程、任务5】加入Main Queue中,异步线程中的任务是:【任务2、同步线程、任务4】。
所以,先执行任务1,然后将异步线程中的任务加入到Global Queue中,因为异步线程,所以任务5不用等待,结果就是2和5的输出顺序不一定。
然后再看异步线程中的任务执行顺序。任务2执行完以后,遇到同步线程。将同步线程中的任务加入到Main Queue中,这时加入的任务3在任务5的后面。
当任务3执行完以后,没有了阻塞,程序继续执行任务4。
从以上的分析来看,得到的几个结果:1最先执行;2和5顺序不一定;4一定在3后面。
总结:
同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!
如果是 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。
如果是 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。
根据queue判断是否新建线程/还是当前线程执行。