平常面试中总会遇见GCD死锁的问题,那些情况下会造成死锁呢,先看一下CGD死锁崩溃的核心源码
__DISPATCH_WAIT_FOR_QUEUE__(dispatch_sync_context_t dsc, dispatch_queue_t dq)
{
uint64_t dq_state = _dispatch_wait_prepare(dq);
if (unlikely(_dq_state_drain_locked_by(dq_state, dsc->dsc_waiter))) {
DISPATCH_CLIENT_CRASH((uintptr_t)dq_state,
"dispatch_sync called on queue"
"already owned by current thread");
}
从上面代码中我们知道,当if条件为真时就会进入crash(这里崩溃的原因这两句英文已经解释了),那什么情况下if条件为真呢,点击_dispatch_lock_is_locked_by
我们进一步分析
_dispatch_lock_is_locked_by(dispatch_lock lock_value, dispatch_tid tid)
{
//lock_value: 当前队列
//tid: 当前线程
// equivalent to _dispatch_lock_owner(lock_value) == tid
return ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0;
}
lock_value
当前传进来的队列,
tid
当前所在的线程
DLOCK_OWNER_MASK
: 宏 ((dispatch_lock)0xfffffffc)
一个很大的数字
分析 ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0
当一个数 &(与上) DLOCK_OWNER_MASK
== 0 那这个数一定是0, 所以(lock_value ^ tid)
为0
(lock_value ^(异或) tid)
为0 也就说明 lock_value 和 tid 相等,也就是当前的队列和当前的线程要有关联 并是不是意思上的相等。(可以理解为同一个线程下同一个队列)
当 ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0
成立时,if条件为真就会促发崩溃 。
从上可以得出一个结论:
在当前线程中同步(sync)的向同一个串行队列添加任务时就会死锁崩溃。
举几个死锁例子来深刻理解上述结论的意思:
以下代码都在viewDidLoad中执行
- (void)viewDidLoad {
[super viewDidLoad];
//结论:在当前线程中同步(sync)的向同一个串行队列添加任务时就会死锁崩溃。
//例子1
dispatch_sync(dispatch_get_main_queue(), ^{
1 sync(同步)
2 和 viewDidLoad 都是主线程
3 和 viewDidLoad 都是主队列 dispatch_get_main_queue
4 主队列都是 串行队列
});
//例子2
dispatch_queue_t lrQueue = dispatch_queue_create("xx", DISPATCH_QUEUE_SERIAL);
dispatch_sync(lrQueue, ^{
当前主线程
dispatch_sync(lrQueue, ^{
1 添加的任务为同步 sync
2 都是同一个串行队列 lrQueue
3 都在同一个线程中
});
});
//例子3
dispatch_async(lrQueue, ^{
当前子线程
dispatch_sync(lrQueue, ^{
1 添加的任务为同步 sync
2 都是同一个串行队列 lrQueue
3 添加的任务都在同一个子线程中, sync是不具备开启线程的能力的
});
});
//例子4
dispatch_sync(lrQueue, ^{
//当前主线程
dispatch_sync(dispatch_get_main_queue(), ^{
1 添加的任务为同步 sync
2 这里是 dispatch_get_main_queue 主队列,和 viewDidLoad 的队列相等
3 都在同一个主线程中
});
});
修改一下例子4
dispatch_async(lrQueue, ^{
当前子线程
dispatch_sync(dispatch_get_main_queue(), ^{
1 添加的任务为同步 sync
2 这里是 dispatch_get_main_queue 主队列,和 viewDidLoad的队列相等
3 viewDidLoad 是在主线程, 但是添加的任务是在子线程中 (条件不满足)
4 sync是不会开启线程的 async才会开启线程
所以条件不满足不是同一个线程 不会崩溃
});
});
}
综上只满足 在同一个线程中,同步的向同一个串行队列中添加任务就会崩溃。