iOS多線程梳理-GCD死鎖

上一篇 iOS多線程梳理-GCD

GCD死鎖探討

先看一個簡單程序:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSLog(@"開始運行%@",[NSThread currentThread]);
    [self longTimeTask];
    NSLog(@"運行完畢%@",[NSThread currentThread]);
}

- (void)longTimeTask{
    NSLog(@"任務開始執(zhí)行%@",[NSThread currentThread]);
    sleep(10);
    NSLog(@"任務執(zhí)行完畢%@",[NSThread currentThread]);
}
/*輸出結果:
2017-10-12 09:46:45.176989+0800 GCDLearnAdvanced[910:54133] 開始運行<NSThread: 0x608000065d40>{number = 1, name = main}
2017-10-12 09:46:45.177220+0800 GCDLearnAdvanced[910:54133] 任務開始執(zhí)行<NSThread: 0x608000065d40>{number = 1, name = main}
2017-10-12 09:46:55.178354+0800 GCDLearnAdvanced[910:54133] 任務執(zhí)行完畢<NSThread: 0x608000065d40>{number = 1, name = main}
2017-10-12 09:46:55.178486+0800 GCDLearnAdvanced[910:54133] 運行完畢<NSThread: 0x608000065d40>{number = 1, name = main}
*/

可以看出,viewDidLoad執(zhí)行了第一句打印后竞阐,會跳入longTimeTask這個函數(shù)執(zhí)行,直到longTimeTask執(zhí)行完畢,才會返回到viewDidLoad中繼續(xù)執(zhí)行下一句闸度。這里重點是程序是順序執(zhí)行的,只有執(zhí)行完上一個后才會繼續(xù)往下執(zhí)行蚜印。

看一個死鎖案例:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSLog(@"開始運行%@",[NSThread currentThread]);
    dispatch_queue_t serialMQ = dispatch_get_main_queue();
    dispatch_sync(serialMQ, ^{
        NSLog(@"task %@",[NSThread currentThread]);
    });
    NSLog(@"運行完畢%@",[NSThread currentThread]);
}
/*輸出結果:
2017-10-12 09:59:13.885921+0800 GCDLearnAdvanced[1007:72128] 開始運行<NSThread: 0x608000075540>{number = 1, name = main}
(lldb) 
*/

可以看到莺禁,只打印“開始運行”就再也沒有響應了,已經(jīng)造成了死鎖窄赋。
分析程序理論執(zhí)行:viewDidLoad開始執(zhí)行哟冬,首先打印“開始運行”,接著執(zhí)行dispatch_sync()函數(shù)寝凌,dispatch_sync把一個block添加到主隊列中柒傻,等待dispatch_sync()執(zhí)行完畢后 viewDidLoad繼續(xù)往下執(zhí)行--打印“運行完畢”。而實際卻只打印了“開始運行”较木,那么肯定是dispatch_sync()函數(shù)出現(xiàn)了問題红符。那么dispatch_sync()出了什么問題呢,繼續(xù)分析

dispatch_sync() API解釋:Submits a block object for execution on a dispatch queue and waits until that block completes.
也就是這個block不執(zhí)行完的話這個函數(shù)永遠沒有返回伐债,也就是永遠沒有執(zhí)行完畢预侯。
主隊列dispatch_get_main_queue() ,是特殊的串行隊列峰锁,任務會先進先出的一個一個在主線程執(zhí)行萎馅。程序中的所有內(nèi)容都在主隊列上運行,除了顯式地發(fā)送到另一個隊列的代碼除外虹蒋。在主隊列的代碼總是運行在主線程上

深入分析死鎖原因糜芳,在此案例中,viewDidLoad先被加入主隊列魄衅,隨后dispatch_sync()再把一個block加入主隊列峭竣,遵循先進先出原理,viewDidLoad相當于隊列的隊頭晃虫,block要想執(zhí)行必須等待viewDidLoad執(zhí)行完畢后才會執(zhí)行皆撩。但是對于viewDidLoad函數(shù)來說 dispatch_sync函數(shù)中block不執(zhí)行完這個函數(shù)就相當于永遠沒有執(zhí)行完畢,viewDidLoad函數(shù)沒辦法繼續(xù)往下執(zhí)行哲银。這樣就造成了相互等待扛吞,viewDidLoad函數(shù)等待dispatch_sync函數(shù)執(zhí)行完畢呻惕,它才能繼續(xù)往下執(zhí)行;dispatch_sync函數(shù)等待viewDidLoad執(zhí)行完畢滥比,這樣它的block才能執(zhí)行亚脆,最終導致死鎖。

再分析一個案例:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSLog(@"開始運行%@",[NSThread currentThread]);
    dispatch_queue_t serialQ = dispatch_queue_create("customSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(serialQ, ^{
         NSLog(@"任務 A %@",[NSThread currentThread]);
    });
    NSLog(@"運行完畢%@",[NSThread currentThread]);
}
/*輸出結果:
2017-10-12 11:08:16.259113+0800 GCDLearnAdvanced[1289:165769] 開始運行<NSThread: 0x60c000065040>{number = 1, name = main}
2017-10-12 11:08:16.259387+0800 GCDLearnAdvanced[1289:165769] 任務 A <NSThread: 0x60c000065040>{number = 1, name = main}
2017-10-12 11:08:16.259510+0800 GCDLearnAdvanced[1289:165769] 運行完畢<NSThread: 0x60c000065040>{number = 1, name = main}
*/

可以看到程序正常執(zhí)行守呜,沒有造成死鎖型酥。為什么呢?根據(jù)主隊列同步死鎖原因查乒,我們很容易就能分析出弥喉,dispatch_sync函數(shù)中的block任務是在自定義串行隊列中分配執(zhí)行的,所以不會死鎖玛迄。
viewDidLoad執(zhí)行到dispatch_sync函數(shù)等待其運行完畢時由境,dispatch_sync()向“customSerial”串行隊列添加了一個同步block,而在“customSerial”串行隊列中蓖议,block的執(zhí)行不需要等待viewDidLoad的執(zhí)行虏杰,因為它們并沒有在同一個隊列中。這樣block中任務在主線程執(zhí)行完畢后函數(shù)返回勒虾,viewDidLoad繼續(xù)正常往下執(zhí)行(造成死鎖需要互相等待 死鎖_百度百科

再分析最后一個案例:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSLog(@"開始運行%@",[NSThread currentThread]);
    dispatch_queue_t serialQ = dispatch_queue_create("customSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQ, ^{
        NSLog(@"任務 A Begin %@",[NSThread currentThread]);
        dispatch_sync(serialQ, ^{
            NSLog(@"任務B %@",[NSThread currentThread]);
        });
        NSLog(@"任務 A End %@",[NSThread currentThread]);
    });
    NSLog(@"運行完畢%@",[NSThread currentThread]);
}
/*輸出結果:
2017-10-12 11:16:05.993607+0800 GCDLearnAdvanced[1337:174414] 開始運行<NSThread: 0x60000007dbc0>{number = 1, name = main}
2017-10-12 11:16:05.993888+0800 GCDLearnAdvanced[1337:174414] 運行完畢<NSThread: 0x60000007dbc0>{number = 1, name = main}
2017-10-12 11:16:05.993914+0800 GCDLearnAdvanced[1337:174445] 任務 A Begin <NSThread: 0x60c000469900>{number = 3, name = (null)}
(lldb) 
*/

可以看到程序執(zhí)行完“任務A Begin”就死鎖了纺阔。
分析函數(shù),在viewDidLoad執(zhí)行一個異步+串行任務A修然,任務A執(zhí)行一個同步+串行任務B笛钝。我們可以看到程序是在dispatch_sync這個地方死鎖了。為什么這個同步+自定義串行隊列會死鎖呢愕宋,上面案例就沒有死鎖玻靡?看明白的小伙伴應該就能發(fā)現(xiàn)死鎖的特點了,在同一串行隊列添加同步任務就會造成死鎖中贝。
分析這個案例囤捻,在任務A中執(zhí)行dispatch_sync函數(shù)時,dispatch_sync函數(shù)向串行隊列“customSerial”添加了一個block任務B邻寿,串行隊列“customSerial”一次執(zhí)行一個任務的蝎土,dispatch_sync()中的block任務B必須等到前一個任務執(zhí)行完畢,可“customSerial”正在執(zhí)行的任務就是被dispatch_sync()阻塞了的那個绣否,于是又發(fā)生了死鎖瘟则。

最終我們得出的結論是 在同一個串行隊列中添加同步任務會導致死鎖

下一篇 iOS多線程梳理-GCD(3)

相關參考:
https://stackoverflow.com/questions/41312006/sync-call-serial-queue-created-in-main-is-ok-while-sync-call-main-queue-in-main
http://www.reibang.com/p/bbabef8aa1fe

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末枝秤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子慷嗜,更是在濱河造成了極大的恐慌淀弹,老刑警劉巖丹壕,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異薇溃,居然都是意外死亡菌赖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門沐序,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琉用,“玉大人,你說我怎么就攤上這事策幼∫厥保” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵特姐,是天一觀的道長晶丘。 經(jīng)常有香客問我,道長唐含,這世上最難降的妖魔是什么浅浮? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮捷枯,結果婚禮上滚秩,老公的妹妹穿的比我還像新娘。我一直安慰自己淮捆,他們只是感情好郁油,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著争剿,像睡著了一般已艰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚕苇,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天哩掺,我揣著相機與錄音,去河邊找鬼涩笤。 笑死嚼吞,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蹬碧。 我是一名探鬼主播舱禽,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼恩沽!你這毒婦竟也來了誊稚?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎里伯,沒想到半個月后城瞎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡疾瓮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年脖镀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狼电。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜒灰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肩碟,到底是詐尸還是另有隱情强窖,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布腾务,位于F島的核電站毕骡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏岩瘦。R本人自食惡果不足惜未巫,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望启昧。 院中可真熱鬧叙凡,春花似錦、人聲如沸密末。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽严里。三九已至新啼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刹碾,已是汗流浹背燥撞。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留迷帜,地道東北人物舒。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像戏锹,于是被迫代替她去往敵國和親冠胯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

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