有這樣一個(gè)例子匪补,即在主線程開啟同步任務(wù)死鎖的例子:
NSLog(@"1"); // 任務(wù)1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2"); // 任務(wù)2
});
NSLog(@"3"); // 任務(wù)3
關(guān)于這個(gè)例子如何會(huì)死鎖昼蛀,網(wǎng)上也有很詳細(xì)的解釋哀军。不過可能對(duì)于某些基礎(chǔ)不是很扎實(shí)的同學(xué)來說阿逃,有些地方不太容易理解。這里,我說一下自己的理解,希望對(duì)你有所幫助信粮。
如大家所說,造成這種死鎖的原因在于:
1.dispatch_sync趁啸,同步執(zhí)行强缘;
2.dispatch_get_main_queue(),主隊(duì)列不傅。這里先說一下為什么會(huì)造成死鎖旅掂,后面再介紹其他內(nèi)容;
第一點(diǎn)蛤签,先讓我們來看看dispatch_sync和dispatch_async辞友,按照字面意思理解前者是同步的栅哀,后者是異步的震肮。蘋果給出的文檔中,dispatch_sync的解釋是:Submits a block for synchronous execution on a dispatch queue留拾。翻譯之后就是戳晌,向隊(duì)列中,提交一個(gè)同步執(zhí)行的block痴柔。同時(shí)沦偎,文檔中也有這樣一句話:dispatch_sync() will not return until the block has finished。就是說咳蔚,只有當(dāng)block中的內(nèi)容執(zhí)行完之后豪嚎,才會(huì)返回之前插入的地方繼續(xù)執(zhí)行。
相對(duì)應(yīng)的谈火,dispatch_async的解釋是:Submits a block for asynchronous execution on a dispatch queue侈询。翻譯之后就是,向隊(duì)列中糯耍,提交一個(gè)異步執(zhí)行的block扔字。同樣的囊嘉,此時(shí)不會(huì)等待block的執(zhí)行,會(huì)直接執(zhí)行之后的代碼革为,而將block交給其他線程扭粱。這里暫且不說,后面再聊震檩。
第二點(diǎn)琢蛤,dispatch_get_main_queue(),蘋果給出的解釋是:Returns the default queue that is bound to the main thread抛虏。也就是說虐块,它返回了依靠主線程來執(zhí)行任務(wù)的隊(duì)列。這里涉及到Runloop嘉蕾,簡單理解就是贺奠,iOS程序有一個(gè)一直在執(zhí)行的線程,這個(gè)線程會(huì)一直運(yùn)行直到被叫停错忱。這個(gè)線程和主隊(duì)列是綁定的儡率,就是用來執(zhí)行主隊(duì)列的任務(wù)。
那么現(xiàn)在把它們放在一起考慮以清,系統(tǒng)一直在順序執(zhí)行主隊(duì)列的任務(wù)(通過主線程)儿普,此時(shí)阻塞主線程(dispatch_sync)向主隊(duì)列隊(duì)尾添加一個(gè)任務(wù)(不知道主隊(duì)列此時(shí)有沒有任務(wù)在執(zhí)行,不care)掷倔。dispatch_sync必須等到block執(zhí)行完才會(huì)返回當(dāng)前線程(主線程)繼續(xù)往下執(zhí)行眉孩,當(dāng)然下一個(gè)任務(wù)仍然來自于主隊(duì)列。那么勒葱,此時(shí)主線程在等待主隊(duì)列給出下一個(gè)任務(wù)(因?yàn)橹骶€程與主隊(duì)列是綁定的浪汪,只能根據(jù)FIFO原則順序執(zhí)行主隊(duì)列的任務(wù));可是主隊(duì)列也在等待凛虽,它在等待主線程將block執(zhí)行完成才會(huì)給主線程另外一個(gè)任務(wù)死遭。主線程和主隊(duì)列在互相等待,那么就造成了死鎖凯旋。
這個(gè)原理確實(shí)很繞口呀潭,在看了很多博客之后,總算有點(diǎn)兒眉目至非。本來我是無法理解為什么會(huì)造成死鎖的钠署,直到想通了上面關(guān)節(jié),就是主線程和主隊(duì)列互相等待荒椭。在想通這些的過程中谐鼎,我做了另外的工作來證實(shí)這種想法,或者說另外的這些讓我想通了這個(gè)關(guān)節(jié)戳杀。讓我們來看另外兩個(gè)例子该面。
這里有一個(gè)不會(huì)死鎖的例子:
NSLog(@"1,NSThread:%@",[NSThread currentThread]); // 任務(wù)1
dispatch_async(queueC, ^{
NSLog(@"2,NSThread:%@",[NSThread currentThread]); // 任務(wù)2
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3,NSThread:%@",[NSThread currentThread]); // 任務(wù)3
});
NSLog(@"4,NSThread:%@",[NSThread currentThread]); // 任務(wù)4
});
NSLog(@"5,NSThread:%@",[NSThread currentThread]); // 任務(wù)5
輸出結(jié)果是:
在這個(gè)例子中夭苗,用到了 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"3,NSThread:%@",[NSThread currentThread]); // 任務(wù)3 });
然而并沒有出現(xiàn)線程死鎖現(xiàn)象,控制臺(tái)正常打印了1 5 2 3 4 隔缀。那么為什么這里不會(huì)造成死鎖呢题造?原因就在于dispatch_sync是阻塞了當(dāng)前線程來給后面隊(duì)列添加任務(wù)。也就是說猾瘸,在這里界赔,dispatch_sync阻塞了number = 3 的線程,將block添加入主隊(duì)列牵触,之后由主線程(與主隊(duì)列綁定)執(zhí)行打印任務(wù)淮悼。接著完成之后,再由*number = 3 *的線程執(zhí)行任務(wù)4揽思,那么當(dāng)然不會(huì)造成線程死鎖袜腥。
下面有一個(gè)會(huì)死鎖的例子:
dispatch_queue_t queueS = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL); // 串行隊(duì)列
NSLog(@"1,NSThread:%@",[NSThread currentThread]); // 任務(wù)1
dispatch_async(queueS, ^{
NSLog(@"2,NSThread:%@",[NSThread currentThread]); // 任務(wù)2
dispatch_sync(queueS, ^{
NSLog(@"3,NSThread:%@",[NSThread currentThread]); // 任務(wù)3
});
NSLog(@"4,NSThread:%@",[NSThread currentThread]); // 任務(wù)4
});
NSLog(@"5,NSThread:%@",[NSThread currentThread]); // 任務(wù)5
它的輸出是:
我們看到,它在執(zhí)行到
dispatch_sync(queueS, ^{ NSLog(@"3,NSThread:%@",[NSThread currentThread]); // 任務(wù)3 });
的時(shí)候钉汗,出現(xiàn)了死鎖羹令,回頭看一下它的打印結(jié)果,任務(wù)1和任務(wù)5都是主線程執(zhí)行的损痰,而任務(wù)2是number = 3 的線程執(zhí)行的福侈,也就是說,dispatch_sync阻塞了number = 3 的線程卢未,同時(shí)肪凛,這個(gè)線程執(zhí)行隊(duì)列queueS里面的任務(wù)**。這就等價(jià)于在主線程向主隊(duì)列同步插入block造成死鎖辽社,因?yàn)榫€程和隊(duì)列相互等待伟墙。
弄清楚線程和隊(duì)列的關(guān)系,這個(gè)問題就變得很簡單了不是嗎爹袁?