本文目錄:
- 同步執(zhí)行主隊(duì)列(在主線(xiàn)程中)
- 另幾種死鎖情況
本文參考文章鏈接:
GCD2之死鎖
學(xué)習(xí)GCD贺嫂,然后搞的我有點(diǎn)死鎖了。下面是研究的一點(diǎn)死鎖案例和原理...嗯...感覺(jué)還是總結(jié)的不太到位匣椰,可以看看第一個(gè)文章鏈接
同步執(zhí)行主隊(duì)列(在主線(xiàn)程中)
在主線(xiàn)程中东羹,同步執(zhí)行主隊(duì)列趁舀,會(huì)造成死鎖早龟。
分析:在主線(xiàn)程,當(dāng)前隊(duì)列為主隊(duì)列扛拨,然后使用同步執(zhí)行提交任務(wù)到主隊(duì)列耘分,死鎖。下面有詳細(xì)的原理解釋鬼癣。
- (void)viewDidLoad {
[super viewDidLoad];
//主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"開(kāi)始");
//異步提交一個(gè)任務(wù)到串行隊(duì)列
dispatch_sync(mainQueue, ^{
NSLog(@"同步執(zhí)行");
});
NSLog(@"結(jié)束");
}
輸出:
2018-08-17 11:21:04.826284+0800 GCD[882:81994] 開(kāi)始
然后就崩潰了
原理分析
- 同步執(zhí)行:必須執(zhí)行完當(dāng)前任務(wù)才能繼續(xù)往下走陶贼,并且不具備開(kāi)啟多線(xiàn)程的能力,只能交給當(dāng)前線(xiàn)程來(lái)執(zhí)行(如果隊(duì)列是主隊(duì)列待秃,會(huì)交給主線(xiàn)程來(lái)執(zhí)行)拜秧。
- 主隊(duì)列,只能交給主線(xiàn)程來(lái)執(zhí)行章郁。平常的程序枉氮,不使用多線(xiàn)程的情況下志衍,一般都是放在主隊(duì)列中的。
現(xiàn)在聊替,我們將viewDidLoad{}看作是一個(gè)任務(wù)楼肪,它是在主隊(duì)列中的,交給主線(xiàn)程來(lái)執(zhí)行的惹悄,它排在主隊(duì)列的隊(duì)首春叫。
現(xiàn)在主線(xiàn)程正在執(zhí)行將這個(gè)任務(wù)viewDidLoad{},主線(xiàn)程在這個(gè)任務(wù)里遇到了一個(gè)同步執(zhí)行泣港,同步執(zhí)行必須將任務(wù)完成才能繼續(xù)往下走( 這里的任務(wù)指NSLog(@"同步執(zhí)行"); )暂殖,然后同步執(zhí)行將這個(gè)任務(wù)提交給了主隊(duì)列,想讓主線(xiàn)程先完成這個(gè)任務(wù)当纱。
但是呛每,注意了,但是坡氯,主隊(duì)列里面有一個(gè)任務(wù)viewDidLoad{}了晨横,同步執(zhí)行將這個(gè)任務(wù)( NSLog(@"同步執(zhí)行"); )提交給主隊(duì)列后,只能排在任務(wù)viewDidLoad{}后面箫柳。主線(xiàn)程必須執(zhí)行完任務(wù)viewDidLoad{}手形,才能執(zhí)行任務(wù)( NSLog(@"同步執(zhí)行"); ),但是同步執(zhí)行要求必須先執(zhí)行完任務(wù)( NSLog(@"同步執(zhí)行"); )滞时,才能繼續(xù)往下走叁幢。主線(xiàn)程說(shuō),我得按順序執(zhí)行啊坪稽,執(zhí)行完前面的任務(wù),才能執(zhí)行后面的鳞骤;同步執(zhí)行說(shuō)窒百,不行,就得先執(zhí)行我的豫尽,才能繼續(xù)下去篙梢。好吧,結(jié)果就這樣卡住了美旧,誰(shuí)也執(zhí)行不了渤滞。
這就是死鎖。
怎么在當(dāng)前情況下怎么解開(kāi)這個(gè)鎖呢榴嗅?
改用異步執(zhí)行妄呕。因?yàn)楫惒綀?zhí)行會(huì)立即返回,不管任務(wù)到底是怎么被執(zhí)行的嗽测。過(guò)程:主線(xiàn)程先執(zhí)行主隊(duì)列里的任務(wù)viewDidLoad{}绪励,在這個(gè)任務(wù)里遇到了異步執(zhí)行肿孵,異步執(zhí)行立即返回,任務(wù)繼續(xù)疏魏,輸出NSLog(@"結(jié)束"); 停做,然后任務(wù)viewDidLoad{}執(zhí)行完成;同時(shí)大莫,在異步執(zhí)行返回的同時(shí)蛉腌,異步執(zhí)行將任務(wù)( NSLog(@"同步執(zhí)行"); )提交給了主隊(duì)列,主隊(duì)列在執(zhí)行完viewDidLoad{}后只厘,就執(zhí)行任務(wù)( NSLog(@"同步執(zhí)行"); )烙丛,輸出NSLog(@"同步執(zhí)行")。結(jié)束懈凹。
改用其它隊(duì)列:使用 dispatch_queue_create 創(chuàng)建一個(gè)隊(duì)列蜀变。同步執(zhí)行將任務(wù)提交給一個(gè)其它的隊(duì)列,這里同步執(zhí)行當(dāng)前線(xiàn)程介评,即主線(xiàn)程執(zhí)行任務(wù)库北。主線(xiàn)程在執(zhí)行任務(wù)viewDidLoad{}時(shí),碰到了同步執(zhí)行们陆,同步執(zhí)行必須執(zhí)行完( NSLog(@"同步執(zhí)行");)才能繼續(xù)寒瓦,同時(shí)這個(gè)任務(wù)提交給了其它的隊(duì)列,主線(xiàn)程切換隊(duì)列坪仇,先執(zhí)行其它隊(duì)列里的這個(gè)任務(wù)( NSLog(@"同步執(zhí)行");)杂腰,在切回到主隊(duì)列繼續(xù)執(zhí)行任務(wù)viewDidLoad{}。
上兩個(gè)方法混用椅文,就不贅述了喂很。
另幾種死鎖情況
- 嵌套兩個(gè)同步執(zhí)行,且同步執(zhí)行的是同一個(gè)串行隊(duì)列皆刺,會(huì)出現(xiàn)死鎖
同步執(zhí)行嵌套少辣,同一個(gè)串行隊(duì)列:
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(serialQueue, ^{
NSLog(@"2 %@", [NSThread currentThread]);
});
NSLog(@"3 %@", [NSThread currentThread]);
});
- 但如果兩個(gè)串行隊(duì)列不是同一個(gè),就不會(huì)出現(xiàn)死鎖羡蛾。
同步執(zhí)行嵌套漓帅,不為同一個(gè)串行隊(duì)列,不會(huì)死鎖:
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue1, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(serialQueue2, ^{
NSLog(@"2 %@", [NSThread currentThread]);
});
NSLog(@"3 %@", [NSThread currentThread]);
});
NSLog(@"4 %@", [NSThread currentThread]);
- 上面這種情況不會(huì)出現(xiàn)死鎖痴怨,而且輸出按照順序忙干。這里我出現(xiàn)了疑惑,不是同步執(zhí)行必須等到block的結(jié)果返回浪藻,才能繼續(xù)嗎?可以在同步執(zhí)行里面捐迫,使用同步執(zhí)行去執(zhí)行嗎?我再試驗(yàn)了一下珠移,發(fā)現(xiàn)弓乙,里面的串行隊(duì)列換成并發(fā)隊(duì)列末融,也不會(huì)出現(xiàn)死鎖。
同步執(zhí)行嵌套暇韧,外面為串行隊(duì)列勾习,里面為并發(fā)隊(duì)列,不會(huì)死鎖:
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("first", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(serialQueue, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(concurrentQueue, ^{
NSLog(@"2 %@", [NSThread currentThread]);
});
NSLog(@"3 %@", [NSThread currentThread]);
});
NSLog(@"4 %@", [NSThread currentThread]);
- 然后我又在別人的博客上發(fā)現(xiàn)了一種死鎖...我越來(lái)越懷疑自己了...
外面是異步串行隊(duì)列懈玻,里面是同步串行隊(duì)列巧婶,串行隊(duì)列為同一個(gè),死鎖:
dispatch_queue_t serialQueue = dispatch_queue_create("fff", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(serialQueue, ^{
NSLog(@"2");
dispatch_sync(serialQueue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
這種情況涂乌,也會(huì)發(fā)生死鎖艺栈,死鎖在里面的同步執(zhí)行。分析:同一個(gè)串行隊(duì)列湾盒,外面是異步湿右,可以開(kāi)啟另一條線(xiàn)程,但是串行隊(duì)列一次只能執(zhí)行一個(gè)任務(wù)罚勾,所以里面的同步執(zhí)行死鎖了毅人。
****然后我總結(jié)了一下,下面是我自己的理解尖殃,可能不對(duì)丈莺,煩請(qǐng)指正****。
在同步執(zhí)行的任務(wù)里送丰,是可以再異步或同步執(zhí)行的缔俄。
并發(fā)隊(duì)列不會(huì)造成死鎖
注意,并發(fā)隊(duì)列不會(huì)造成死鎖器躏。****因?yàn)椴l(fā)隊(duì)列可以一次執(zhí)行多個(gè)任務(wù)****俐载。
- 只有串行隊(duì)列會(huì)造成死鎖,因?yàn)榇嘘?duì)列一次只能執(zhí)行隊(duì)列的一個(gè)任務(wù)登失,當(dāng)你在執(zhí)行這個(gè)任務(wù)時(shí)瞎疼,又提交了一個(gè)任務(wù)到這個(gè)串行隊(duì)列,并要求同步執(zhí)行時(shí)壁畸,會(huì)出現(xiàn)死鎖。
上面的第一個(gè)例子就是這樣茅茂, 主隊(duì)列(是串行隊(duì)列)正在被主線(xiàn)程執(zhí)行任務(wù)捏萍,這時(shí)要求同步執(zhí)行,并提交了一個(gè)任務(wù)到主隊(duì)列空闲,出現(xiàn)死鎖令杈。
中間那個(gè)死鎖的例子和上面的原理相同。
最后的一個(gè)死鎖的例子碴倾。雖然外面是異步執(zhí)行逗噩,但是串行隊(duì)列要求執(zhí)行完這個(gè)掉丽,才能執(zhí)行下一個(gè)。異步執(zhí)行的那個(gè)任務(wù)還沒(méi)完异雁,就要求同步執(zhí)行下一個(gè)任務(wù)捶障;串行隊(duì)列的第一個(gè)任務(wù)還沒(méi)出去呢,就要求執(zhí)行第二個(gè)纲刀,這肯定不行项炼。
再看另一個(gè)死鎖的例子:
dispatch_queue_t concurrentQueue = dispatch_queue_create("first", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_sync(concurrentQueue, ^{
NSLog(@"2 %@", [NSThread currentThread]);
dispatch_sync(serialQueue, ^{
NSLog(@"3 %@", [NSThread currentThread]);
});
});
});
中間的換成異步執(zhí)行就不會(huì)死鎖:
dispatch_sync(serialQueue, ^{
NSLog(@"1 %@", [NSThread currentThread]);
dispatch_async(concurrentQueue, ^{
NSLog(@"2 %@", [NSThread currentThread]);
dispatch_sync(serialQueue, ^{
NSLog(@"3 %@", [NSThread currentThread]);
});
});
});
我也是閑的...搞的我有點(diǎn)糊涂了,最后還是說(shuō)示绊,死鎖得具體問(wèn)題具體分析锭部。
****總結(jié)一點(diǎn),死鎖歸根到底面褐,就是串行隊(duì)列被阻塞了****拌禾。
大家可以看看這篇文章:理解GCD死鎖