您的位置:首页 > 其它

关于GCD线程死锁的一点儿理解

2018-03-17 19:26 260 查看
上一篇在总结 GCD 的时候读到了一篇博客,提到了这么一个问题:既然在主队列(dispatch_get_main_queue)中同步(dispatch_sync())执行一个任务会造成死锁,- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync----%@",[NSThread currentThread]);
});
NSLog(@"%@",[NSThread currentThread]);
}为什么在一个全局队列中同步执行任务不会造成死锁?- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@",[NSThread currentThread]);
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"sync----%@",[NSThread currentThread]);
});
NSLog(@"%@",[NSThread currentThread]);
}原文是这么写的“仔细分析一下:主线程先执行第一句打印,然后执行dispatch_sync方法,往全局队列中添加一个同步任务,此时主线程应该阻塞住,等待全局队列中同步任务的执行完毕,而全局队列dispatch_sync方法并不会创建新线程,那肯定需要在主线程执行(上一篇的结论),可是主线程已经阻塞了,所以按理说此时应该发生死锁,可是为什么从打印结果看它确实是在主线程执行的同步任务,而且并没有发生死锁现象呢?”,如果不深入分析,貌似说的也不错,主线程已经阻塞了,肯定会造成死锁,但是为什么没造成死锁呢?
其实稍微深入分析一下,就会发现错的有多离谱。如果按照这样的逻辑,同步任务一旦阻塞了主线程,就会造成死锁,那么OC就没得玩了。因为在OC的程序中,主线程是一直都存在的,一直在循环着处理各种任务,除非是关闭了应用。
而这里之所以没有造成死锁,是因为同步任务阻塞的并不是当前线程(在这个例子中就是主线程),而是阻塞了当前的队列。举个例子就很明白了。// 串行队列同步执行
-(void)serialQueueSync{
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("firstSerialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"现在开始执行了--%@",[NSThread currentThread]);

//添加两个任务到队列中 同步执行
dispatch_sync(queue, ^{
NSLog(@"+++++++%@",[NSThread currentThread]);
});
NSLog(@"打印+结束");

dispatch_sync(queue, ^{
NSLog(@"_______%@",[NSThread currentThread]);
});
NSLog(@"打印-结束");

}
2018-03-17 18:37:14.199 3[4786:495520] 现在开始执行了--<NSThread: 0x60000006b080>{number = 1, name = main}
2018-03-17 18:37:14.199 3[4786:495520] +++++++<NSThread: 0x60000006b080>{number = 1, name = main}
2018-03-17 18:37:14.199 3[4786:495520] 打印+结束
2018-03-17 18:37:14.200 3[4786:495520] _______<NSThread: 0x60000006b080>{number = 1, name = main}
2018-03-17 18:37:14.200 3[4786:495520] 打印-结束
从打印结果中就可以很明显的看出来,主线程在打印完第一句话之后遇到了同步任务,并没有被同步任务阻塞,而是去串行队列中(firstSerialQueue)执行任务去了,在串行队列中的同步任务完成后又回到了被阻塞的主队列中继续执行主队列中的任务。所以说同步任务并不会阻塞线程(不管是主线程还是子线程)。虽然主队列和firstSerialQueue都是串行队列,但因为他们不是同一个队列,所以不会造成线程死锁。
也许现在你会问,那为什么在主队列中执行同步任务会造成线程死锁呢?现在我们再来看看那段代码:- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@",[NSThread currentThread]);//任务1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync----%@",[NSThread currentThread]);//任务2
});
NSLog(@"%@",[NSThread currentThread]);//任务3
}从代码中可以看出,这个同步任务是把任务加到了主队列中,也就是加到被自己阻塞的队列中了,而恰好这个主队列又是一个串行队列,串行队列遵循着‘先进先出’的原则,所以才会造成死锁的。具体来讲就是一个矛与盾的问题,因为打印完任务1之后,遇到的同步任务把任务2加到了主队列的后面,也就是任务3的后面,根据串行队列‘先进先出’的原则,肯定是要先执行任务3再执行任务2,就是1->3->2的执行顺序。而同步任务又要求当前线程必须在执行完同步任务也就是任务2之后再回去执行任务3,是1->2->3的顺序,所以这个时候主线程就傻掉了,主线程此时内心是这样想的“我是谁?我从哪里来的?我要去干什么?”,所以就挂掉了。这个跟上面的串行队列同步执行的例子不同的地方在于,同步任务是在同一个串行队列中,所以会死锁。
聪明的你也许会立马发现另外一个问题,如果当前队列不是串行队列,会不会造成线程死锁?那我们稍微改一下代码试试:- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("firstConcurrentQueue", DISPATCH_Q
945b
UEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);//任务1
dispatch_sync(queue, ^{
NSLog(@"sync----%@",[NSThread currentThread]);//任务2
});
NSLog(@"%@",[NSThread currentThread]);//任务3
});
}
2018-03-17 19:12:28.236 3[5311:565511] <NSThread: 0x60800007d300>{number = 1, name = main}
2018-03-17 19:12:28.237 3[5311:565511] sync----<NSThread: 0x60800007d300>{number = 1, name = main}
2018-03-17 19:12:28.237 3[5311:565511] <NSThread: 0x60800007d300>{number = 1, name = main}
这次是把同样的任务放到了并行队列中,但从打印结果中可以看出,并没有造成死锁,这是因为并行队列不要求‘先进先出’,既然同步任务要求先执行任务2,那就先执行任务2好了。
写的有点儿啰嗦了,如果有不对的地方还请大牛指出,万分感谢!(好了,周六加班到现在,我也该去吃饭了

)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: