關(guān)于向主線程添加同步任務(wù)造成死鎖的思考

有這樣一個(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é)果是:

image.png

在這個(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

它的輸出是:

image.png

我們看到,它在執(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è)問題就變得很簡單了不是嗎爹袁?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末远荠,一起剝皮案震驚了整個(gè)濱河市矮固,隨后出現(xiàn)的幾起案子失息,更是在濱河造成了極大的恐慌,老刑警劉巖档址,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盹兢,死亡現(xiàn)場離奇詭異,居然都是意外死亡守伸,警方通過查閱死者的電腦和手機(jī)绎秒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尼摹,“玉大人见芹,你說我怎么就攤上這事剂娄。” “怎么了玄呛?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵阅懦,是天一觀的道長。 經(jīng)常有香客問我徘铝,道長耳胎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任惕它,我火速辦了婚禮怕午,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淹魄。我一直安慰自己郁惜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布甲锡。 她就那樣靜靜地躺著扳炬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搔体。 梳的紋絲不亂的頭發(fā)上恨樟,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音疚俱,去河邊找鬼劝术。 笑死,一個(gè)胖子當(dāng)著我的面吹牛呆奕,可吹牛的內(nèi)容都是我干的养晋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼梁钾,長吁一口氣:“原來是場噩夢啊……” “哼绳泉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起姆泻,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤零酪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后拇勃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體四苇,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年方咆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了月腋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖榆骚,靈堂內(nèi)的尸體忽然破棺而出片拍,到底是詐尸還是另有隱情,我是刑警寧澤妓肢,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布穆碎,位于F島的核電站,受9級(jí)特大地震影響职恳,放射性物質(zhì)發(fā)生泄漏所禀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一放钦、第九天 我趴在偏房一處隱蔽的房頂上張望色徘。 院中可真熱鬧,春花似錦操禀、人聲如沸褂策。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斤寂。三九已至,卻和暖如春揪惦,著一層夾襖步出監(jiān)牢的瞬間遍搞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工器腋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留溪猿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓纫塌,卻偏偏與公主長得像诊县,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子措左,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容