iOS GCD死鎖底層探究和造成死鎖的條件

平常面試中總會遇見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ù)就會崩潰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容