本文目录:
- 同步执行主队列(在主线程中)
- 另几种死锁情况
本文参考文章链接:
GCD2之死锁
学习GCD,然后搞的我有点死锁了。下面是研究的一点死锁案例和原理...嗯...感觉还是总结的不太到位,可以看看第一个文章链接
同步执行主队列(在主线程中)
在主线程中,同步执行主队列,会造成死锁。
分析:在主线程,当前队列为主队列,然后使用同步执行提交任务到主队列,死锁。下面有详细的原理解释。
- (void)viewDidLoad {
[super viewDidLoad];
//主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"开始");
//异步提交一个任务到串行队列
dispatch_sync(mainQueue, ^{
NSLog(@"同步执行");
});
NSLog(@"结束");
}
输出:
2018-08-17 11:21:04.826284+0800 GCD[882:81994] 开始
然后就崩溃了
原理分析
- 同步执行:必须执行完当前任务才能继续往下走,并且不具备开启多线程的能力,只能交给当前线程来执行(如果队列是主队列,会交给主线程来执行)。
- 主队列,只能交给主线程来执行。平常的程序,不使用多线程的情况下,一般都是放在主队列中的。
现在,我们将viewDidLoad{}看作是一个任务,它是在主队列中的,交给主线程来执行的,它排在主队列的队首。
现在主线程正在执行将这个任务viewDidLoad{},主线程在这个任务里遇到了一个同步执行,同步执行必须将任务完成才能继续往下走( 这里的任务指NSLog(@"同步执行"); ),然后同步执行将这个任务提交给了主队列,想让主线程先完成这个任务。
但是,注意了,但是,主队列里面有一个任务viewDidLoad{}了,同步执行将这个任务( NSLog(@"同步执行"); )提交给主队列后,只能排在任务viewDidLoad{}后面。主线程必须执行完任务viewDidLoad{},才能执行任务( NSLog(@"同步执行"); ),但是同步执行要求必须先执行完任务( NSLog(@"同步执行"); ),才能继续往下走。主线程说,我得按顺序执行啊,执行完前面的任务,才能执行后面的;同步执行说,不行,就得先执行我的,才能继续下去。好吧,结果就这样卡住了,谁也执行不了。
这就是死锁。
怎么在当前情况下怎么解开这个锁呢?
改用异步执行。因为异步执行会立即返回,不管任务到底是怎么被执行的。过程:主线程先执行主队列里的任务viewDidLoad{},在这个任务里遇到了异步执行,异步执行立即返回,任务继续,输出NSLog(@"结束"); ,然后任务viewDidLoad{}执行完成;同时,在异步执行返回的同时,异步执行将任务( NSLog(@"同步执行"); )提交给了主队列,主队列在执行完viewDidLoad{}后,就执行任务( NSLog(@"同步执行"); ),输出NSLog(@"同步执行")。结束。
改用其它队列:使用 dispatch_queue_create 创建一个队列。同步执行将任务提交给一个其它的队列,这里同步执行当前线程,即主线程执行任务。主线程在执行任务viewDidLoad{}时,碰到了同步执行,同步执行必须执行完( NSLog(@"同步执行");)才能继续,同时这个任务提交给了其它的队列,主线程切换队列,先执行其它队列里的这个任务( NSLog(@"同步执行");),在切回到主队列继续执行任务viewDidLoad{}。
上两个方法混用,就不赘述了。
另几种死锁情况
- 嵌套两个同步执行,且同步执行的是同一个串行队列,会出现死锁
同步执行嵌套,同一个串行队列:
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(serialQueue, ^{
NSLog(@"2 %@", [NSThread currentThread]);
});
NSLog(@"3 %@", [NSThread currentThread]);
});
- 但如果两个串行队列不是同一个,就不会出现死锁。
同步执行嵌套,不为同一个串行队列,不会死锁:
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue1, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(serialQueue2, ^{
NSLog(@"2 %@", [NSThread currentThread]);
});
NSLog(@"3 %@", [NSThread currentThread]);
});
NSLog(@"4 %@", [NSThread currentThread]);
- 上面这种情况不会出现死锁,而且输出按照顺序。这里我出现了疑惑,不是同步执行必须等到block的结果返回,才能继续吗?可以在同步执行里面,使用同步执行去执行吗?我再试验了一下,发现,里面的串行队列换成并发队列,也不会出现死锁。
同步执行嵌套,外面为串行队列,里面为并发队列,不会死锁:
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("first", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(serialQueue, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(concurrentQueue, ^{
NSLog(@"2 %@", [NSThread currentThread]);
});
NSLog(@"3 %@", [NSThread currentThread]);
});
NSLog(@"4 %@", [NSThread currentThread]);
- 然后我又在别人的博客上发现了一种死锁...我越来越怀疑自己了...
外面是异步串行队列,里面是同步串行队列,串行队列为同一个,死锁:
dispatch_queue_t serialQueue = dispatch_queue_create("fff", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(serialQueue, ^{
NSLog(@"2");
dispatch_sync(serialQueue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
这种情况,也会发生死锁,死锁在里面的同步执行。分析:同一个串行队列,外面是异步,可以开启另一条线程,但是串行队列一次只能执行一个任务,所以里面的同步执行死锁了。
****然后我总结了一下,下面是我自己的理解,可能不对,烦请指正****。
在同步执行的任务里,是可以再异步或同步执行的。
并发队列不会造成死锁
注意,并发队列不会造成死锁。****因为并发队列可以一次执行多个任务****。
- 只有串行队列会造成死锁,因为串行队列一次只能执行队列的一个任务,当你在执行这个任务时,又提交了一个任务到这个串行队列,并要求同步执行时,会出现死锁。
上面的第一个例子就是这样, 主队列(是串行队列)正在被主线程执行任务,这时要求同步执行,并提交了一个任务到主队列,出现死锁。
中间那个死锁的例子和上面的原理相同。
最后的一个死锁的例子。虽然外面是异步执行,但是串行队列要求执行完这个,才能执行下一个。异步执行的那个任务还没完,就要求同步执行下一个任务;串行队列的第一个任务还没出去呢,就要求执行第二个,这肯定不行。
再看另一个死锁的例子:
dispatch_queue_t concurrentQueue = dispatch_queue_create("first", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(concurrentQueue, ^{
NSLog(@"2 %@", [NSThread currentThread]);
dispatch_sync(serialQueue, ^{
NSLog(@"3 %@", [NSThread currentThread]);
});
});
});
中间的换成异步执行就不会死锁:
dispatch_sync(serialQueue, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_async(concurrentQueue, ^{
NSLog(@"2 %@", [NSThread currentThread]);
dispatch_sync(serialQueue, ^{
NSLog(@"3 %@", [NSThread currentThread]);
});
});
});
我也是闲的...搞的我有点糊涂了,最后还是说,死锁得具体问题具体分析。
****总结一点,死锁归根到底,就是串行队列被阻塞了****。
大家可以看看这篇文章:理解GCD死锁