大話GCD中的死鎖

最近看了GCD中的死鎖相關(guān)的內(nèi)容,現(xiàn)結(jié)合其他朋友的貢獻(xiàn)做一些分析和總結(jié)。

首先來(lái)看兩個(gè)概念: 任務(wù)和隊(duì)列

  • 任務(wù)(什么block,函數(shù),以及隨便的代碼片段都可以稱為一個(gè)任務(wù))
    任務(wù)分為同步任務(wù)和異步任務(wù):同步任務(wù)會(huì)阻塞當(dāng)前線程(其實(shí)這個(gè)任務(wù)也就是block中的內(nèi)容),等block中的任務(wù)執(zhí)行完成之后,當(dāng)前線程才會(huì)繼續(xù)往下執(zhí)行;異步任務(wù)不會(huì)阻塞當(dāng)前線程,也就是不需要等block中的任務(wù)執(zhí)行完成,當(dāng)前線程就能直接往下執(zhí)行。

  • 隊(duì)列(任務(wù)調(diào)度的規(guī)則)
    隊(duì)列分為串行隊(duì)列和并行隊(duì)列:串行隊(duì)列中,GCD會(huì)按照先進(jìn)先出的原則,順序執(zhí)行放進(jìn)來(lái)的任務(wù);并行隊(duì)列中,GCD其實(shí)也是按照先進(jìn)先出的原則,只是每個(gè)任務(wù)可能被放進(jìn)了不同的線程,因?yàn)樗俣确浅?欤越o我們的感覺(jué)像是所有的任務(wù)同時(shí)執(zhí)行

兩個(gè)常用的隊(duì)列

  • dispatch_get_global_queue: 全局并發(fā)隊(duì)列,可以同時(shí)提供多個(gè)線程執(zhí)行任務(wù)。
  • dispatch_get_main_queue: 串行主隊(duì)列,只能提供一個(gè)主線程來(lái)執(zhí)行任務(wù)。

下面用實(shí)例來(lái)分析一下常見(jiàn)的死鎖問(wèn)題。

1.同步主隊(duì)列死鎖

- (void)gcdTest1 {
    NSLog(@"1 - %@",[NSThread currentThread]); //1
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2 - %@",[NSThread currentThread]); //2
    });
    NSLog(@"3 - %@",[NSThread currentThread]); //3
    
    //打?。?
 /*
    解釋:dispatch_sync阻塞了當(dāng)前線程,跑去執(zhí)行任務(wù)2,
而任務(wù)2又是被放到主隊(duì)列里面,也就是要在主線程中執(zhí)行了,
但是它是放在了任務(wù)3的后面(不要以為任務(wù)2的代碼寫在了任務(wù)3的前面,任務(wù)2在隊(duì)列中就排在了任務(wù)3的前面)。
由于主隊(duì)列是一個(gè)串行隊(duì)列,任務(wù)2需要等任務(wù)3執(zhí)行完之后,才能被執(zhí)行,而此時(shí)線程已經(jīng)在任務(wù)3之前被阻塞住了,
所以任務(wù)3不可能被執(zhí)行,所以任務(wù)2也不可能被執(zhí)行
     */
}

2.同步全局并行隊(duì)列

- (void)gcdTest2 {
    NSLog(@"1 - %@",[NSThread currentThread]); //1
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2 - %@",[NSThread currentThread]); //2 (通過(guò)打印可以發(fā)現(xiàn),任務(wù)2也是放在主線程中執(zhí)行的,這也就說(shuō)明了全局隊(duì)列是可以拿主線程來(lái)執(zhí)行相應(yīng)任務(wù)的)
    });
    NSLog(@"3 - %@",[NSThread currentThread]); //3
    
    //打?。?,2,3
    /*
       解釋:首先dispatch_sync阻塞當(dāng)前線程,跑去執(zhí)行任務(wù)2,
任務(wù)2是被放到dispatch_get_global_queue這個(gè)并行隊(duì)列里面, 
而任務(wù)3是在主隊(duì)列里面,也就是說(shuō)這兩個(gè)任務(wù)根本就不在同一個(gè)隊(duì)列,
就不存在一個(gè)先后順序的問(wèn)題,所以三個(gè)任務(wù)都會(huì)被執(zhí)行
     */
   }

3.同步自定義串行隊(duì)列

- (void)gcdTest3 {
    NSLog(@"1 - %@",[NSThread currentThread]); //1
    dispatch_sync(dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL), ^{
        NSLog(@"2 - %@",[NSThread currentThread]); //2
    });
    NSLog(@"3 - %@",[NSThread currentThread]);//3
    
    //打印:1,2,3
    /*
      解釋:同上。也就是任務(wù)2和任務(wù)3兩個(gè)任務(wù)處于不同的隊(duì)列,所以沒(méi)有影響,都會(huì)被執(zhí)行。
     */
  }

4.異步主隊(duì)列

- (void)gcdTest4 {
    NSLog(@"1 - %@",[NSThread currentThread]); //1
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"2 - %@",[NSThread currentThread]);//2 (這里打印的還是主線程,說(shuō)明異步并不代表要開(kāi)辟新的線程,)
    });
    NSLog(@"3 - %@",[NSThread currentThread]);//3
    
    //打印:1,3,2
/*
  解釋:dispatch_async是一個(gè)異步任務(wù),不會(huì)阻塞當(dāng)前線程,
所以3可以執(zhí)行,dispatch_get_main_queue是一個(gè)串行隊(duì)列,所以任務(wù)2是排在任務(wù)3的后面
*/    
}

5.異步全局隊(duì)列

- (void)gcdTest5 {
    
        NSLog(@"1 - %@",[NSThread currentThread]); //1
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"2 - %@",[NSThread currentThread]); //2
        });
        NSLog(@"3 - %@",[NSThread currentThread]); //3
    //打印: 1,3,2
    /*
        解釋:這個(gè)跟上面那個(gè)的區(qū)別是:上面那個(gè)任務(wù)2是放到主隊(duì)列里面的,
肯定是排在任務(wù)3的后面,所以打印的結(jié)果是1,3,2.
        但是這個(gè)任務(wù)2是放在一個(gè)全局隊(duì)列里面,而任務(wù)3是放到主隊(duì)列里面,
按理說(shuō)任務(wù)2和任務(wù)3是可以并行的,
也就是說(shuō)任務(wù)3和任務(wù)2的執(zhí)行順序可能不確定,但是我們通過(guò)打印發(fā)現(xiàn),
在執(zhí)行任務(wù)2的時(shí)候開(kāi)辟了新的線程,而開(kāi)辟新的線程需要額外的時(shí)間,所以任務(wù)2一般都會(huì)排在任務(wù)3的后面。
     */
    
}

6.異步并行隊(duì)列2

- (void)gcdTest6 {
    NSLog(@"1 - %@",[NSThread currentThread]); //1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2 - %@",[NSThread currentThread]); //2
    });
    for (int i = 0; i<100; i++) { //3
        NSLog(@"3 - %@",[NSThread currentThread]);
    }
    //打印:任務(wù)2穿插在任務(wù)3循環(huán)中間
    /*
        解釋:上面剛剛說(shuō)過(guò),任務(wù)2會(huì)開(kāi)辟新的線程,需要額外的時(shí)間, 
但是任務(wù)3是一個(gè)大循環(huán),需要更多的時(shí)間。所以從理論上說(shuō),任務(wù)2和任務(wù)3的執(zhí)行順序還是不確定的
     */
    
   }

7.交叉分析

- (void)gcdTest7 {
    NSLog(@"1"); // 任務(wù)1
    dispatch_async(dispatch_get_global_queue(0, 0), ^{ //block1
        NSLog(@"2 - %@",[NSThread currentThread]); // 任務(wù)2
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"3"); // 任務(wù)3
        });
        NSLog(@"4"); // 任務(wù)4
    });
    NSLog(@"5"); // 任務(wù)5
    //打印:1,5,2,3,4
    /*
        解釋:任務(wù)1執(zhí)行完之后,dispatch_async異步去執(zhí)行另外的任務(wù),不會(huì)阻塞當(dāng)前線程,
所以任務(wù)5接著被執(zhí)行,接著任務(wù)2被執(zhí)行,dispatch_sync又阻塞了block1中的線程去執(zhí)行任務(wù)3,
而任務(wù)3是放在主隊(duì)列中的,跟任務(wù)4不是同一個(gè)隊(duì)列,所以任務(wù)3執(zhí)行完了之后,
任務(wù)4也被執(zhí)行了,所以打印的結(jié)果是: 1,5,2,3,4
     */
}
- (void)gcdTest8 {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"1 - %@",[NSThread currentThread]); // 任務(wù)1
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"2 - %@",[NSThread currentThread]); // 任務(wù)2
        });
        NSLog(@"3"); // 任務(wù)3
    });
    NSLog(@"4"); // 任務(wù)4
    while (1) {
    }
    NSLog(@"5"); // 任務(wù)5
   
    //打印結(jié)果:4,1
    /*
        解釋:dispatch_async是一個(gè)異步任務(wù),不會(huì)阻塞當(dāng)前線程,
所以任務(wù)4會(huì)執(zhí)行,然后進(jìn)入block,執(zhí)行任務(wù)1,
接著dispatch_sync會(huì)阻塞block中線程,開(kāi)始執(zhí)行任務(wù)2,
而任務(wù)2是放到主隊(duì)列中的,排在任務(wù)5的后面,
而任務(wù)5又在死循環(huán)的后面,不可能被執(zhí)行,所以任務(wù)2不會(huì)被執(zhí)行,從而3也不會(huì)被完成,所以打印結(jié)果是4,1。
     */
    
}

歡迎大家和我交流溝通,文章中有任何錯(cuò)誤和漏洞,懇請(qǐng)指正,謝謝。

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

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

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