平常面試中總會遇見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");
}
從上面代碼中我們知道,當(dāng)if條件為真時就會進(jìn)入crash(這里崩潰的原因這兩句英文已經(jīng)解釋了),那什么情況下if條件為真呢,點擊_dispatch_lock_is_locked_by我們進(jìn)一步分析
_dispatch_lock_is_locked_by(dispatch_lock lock_value, dispatch_tid tid)
{
//lock_value: 當(dāng)前隊列
//tid: 當(dāng)前線程
// equivalent to _dispatch_lock_owner(lock_value) == tid
return ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0;
}
lock_value 當(dāng)前傳進(jìn)來的隊列,
tid 當(dāng)前所在的線程
DLOCK_OWNER_MASK: 宏 ((dispatch_lock)0xfffffffc)一個很大的數(shù)字
分析 ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0
當(dāng)一個數(shù) &(與上) DLOCK_OWNER_MASK == 0 那這個數(shù)一定是0, 所以(lock_value ^ tid) 為0
(lock_value ^(異或) tid) 為0 也就說明 lock_value 和 tid 相等,也就是當(dāng)前的隊列和當(dāng)前的線程要有關(guān)聯(lián) 并是不是意思上的相等。(可以理解為同一個線程下同一個隊列)
當(dāng) ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0 成立時,if條件為真就會促發(fā)崩潰 。
從上可以得出一個結(jié)論:
在當(dāng)前線程中同步(sync)的向同一個串行隊列添加任務(wù)時就會死鎖崩潰。
舉幾個死鎖例子來深刻理解上述結(jié)論的意思:
以下代碼都在viewDidLoad中執(zhí)行
- (void)viewDidLoad {
[super viewDidLoad];
//結(jié)論:在當(dāng)前線程中同步(sync)的向同一個串行隊列添加任務(wù)時就會死鎖崩潰。
//例子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, ^{
當(dāng)前主線程
dispatch_sync(lrQueue, ^{
1 添加的任務(wù)為同步 sync
2 都是同一個串行隊列 lrQueue
3 都在同一個線程中
});
});
//例子3
dispatch_async(lrQueue, ^{
當(dāng)前子線程
dispatch_sync(lrQueue, ^{
1 添加的任務(wù)為同步 sync
2 都是同一個串行隊列 lrQueue
3 添加的任務(wù)都在同一個子線程中, sync是不具備開啟線程的能力的
});
});
//例子4
dispatch_sync(lrQueue, ^{
//當(dāng)前主線程
dispatch_sync(dispatch_get_main_queue(), ^{
1 添加的任務(wù)為同步 sync
2 這里是 dispatch_get_main_queue 主隊列,和 viewDidLoad 的隊列相等
3 都在同一個主線程中
});
});
修改一下例子4
dispatch_async(lrQueue, ^{
當(dāng)前子線程
dispatch_sync(dispatch_get_main_queue(), ^{
1 添加的任務(wù)為同步 sync
2 這里是 dispatch_get_main_queue 主隊列,和 viewDidLoad的隊列相等
3 viewDidLoad 是在主線程, 但是添加的任務(wù)是在子線程中 (條件不滿足)
4 sync是不會開啟線程的 async才會開啟線程
所以條件不滿足不是同一個線程 不會崩潰
});
});
}
綜上只滿足 在同一個線程中,同步的向同一個串行隊列中添加任務(wù)就會崩潰。