「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」
前言
上一篇,我们写了关于iOS多线程之一:进程,线程,队列的关系,描写了进程
与线程
的关系,线程
与队列
的关系,还有关于异步
,同步
,串行
,并发
的简单使用。那这篇文章,我们就接着上一篇的内容,分析一下关于死锁
,以及堵塞
。
死锁
死锁
简单来说是指2个任务之间互相等待,任务A需要等待任务B执行完后才执行,而任务B也需要等待任务B执行完后才执行,就会造成线程死锁
。
同步主队列死锁
下面我们先来描述一下主队列
死锁的问题,对于这种情况死锁相信面试过的同事,应该都有遇到过。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任务A");
});
NSLog(@"任务B");
}
运行后:没有打印任务B
,并且在dispatch_sync
行报错。
我们先来简单分析一下:
-
viewDidLoad
: 这个方法是在主线程
运行的,主线程
又是执行主队列
的任务的。 -
dispatch_sync
:是同步线程,有这个就不用想了,必造成堵塞
。堵塞就意味着必须先执行完同步线程的任务,才能结束Block
,继续往下面走。 -
任务B
:任务B也是主队列的任务。前面堵塞了,必须等走完任务A,才能轮到任务B。
可是问题出现了啊,出现在哪呢,就出现在任务A。任务A它也是加入主队列的任务。
坏了坏了,主队列是遵循先进先出原则,也就是我们说的FIFO
原理。它必须放在主队列的最后面。也就是说他必须等任务B执行完后,才能轮到它。
苍天饶过谁,任务A在等任务B完成,任务B也在等任务A完成,GG,直接线程死锁。
那如何破解呢?
有3个方法。
dispatch_sync
同步线程,改成dispatch_sync
异步线程。dispatch_syn
的同步线程,任务A就不要加入主队列,可以加入其它队列(串行,并发)都行。- 全部干掉,不做就不会错(开玩笑的)。
同步串行队列死锁
串行队列
造成死锁
的情况有遇到过吗,下面来个例子看看。
- (void)demo
{
dispatch_queue_t queue = dispatch_queue_create("jj.com", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务1:%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"任务2:%@",[NSThread currentThread]);
});
NSLog(@"任务3:%@",[NSThread currentThread]);
});
NSLog(@"主线程执行");
}
先看下结果,的确是出现死锁了。
先来分析一波:
-
dispatch_async
:先来一个异步线程,而且是异步串行,这种情况会创建新的线程,所以我们能看到打印任务1是个子线程。 -
主线程执行
:这里的打印是先走的,没有问题,异步线程是不会影响到主线程执行任务的。 -
dispatch_sync
:又是同步线程,这家伙又堵塞住当前的线程,看截图,我们可以叫线程6。堵塞了线程6啊。 -
任务3
:任务3是在串行队列中的,签名的同步线程堵塞住了,所以任务3需要等待同步线程执行完,线程6才会执行任务3。 -
任务2
:任务2是加入到串行队列的,是同一个串行队列,所以它啊,按照FIFO
原则,需要排在任务3后面,也就是说任务3执行完才能执行任务2。
该来的还是来了,任务3必须等同步线程执行完任务2,任务2在串行队列中必须排再任务3后面,又一个互等,结果无疑是死锁啦。
是不是和第一个例子类似,主队列也是一个特殊的串行队列,所以效果也是一样的。
解决方案:
在同步线程里面,想用串行队列也可以,只要不是同一个即可。
- (void)demo
{
dispatch_queue_t queue = dispatch_queue_create("jj.com", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务1:%@",[NSThread currentThread]);
dispatch_sync(dispatch_queue_create("jj2.com", DISPATCH_QUEUE_SERIAL), ^{
NSLog(@"任务2:%@",[NSThread currentThread]);
});
NSLog(@"任务3:%@",[NSThread currentThread]);
});
NSLog(@"主线程执行");
}
原因还要说吗,说吧,不然怕到时候自己重新看时没记起来。说白了就是这是2个不同的串行队列,串行队列1的任务1执行后,同步线程堵塞了,先把串行队列2的任务交给子线程执行,执行后,串行队列1的任务3再执行。
所以就没有出现死锁
的情况,只因为这是2个不同的队列
,任务2不用等任务3执行了。
总结
在当前串行队列
中,使用sync
同步函数,往当前串行队列中添加任务,会卡在当前的串行队列,从而造成死锁
。
参考文章: