在iOS開(kāi)發(fā)中,當(dāng)遇到網(wǎng)絡(luò)請(qǐng)求和耗時(shí)操作通常需要另外開(kāi)一個(gè)子線程灰伟,然后需要刷新UI的時(shí)候回到主線程刷新宪塔;這里就要用到多線程的技術(shù)第献,iOS多線程通常有四種方式:PThread,NSThread,NSOperation,GCD;在這里主要介紹一下GCD的情況下死鎖的幾種情況,以及簡(jiǎn)單的分析:
注:很多資料來(lái)自前輩的文章俱恶。
1雹嗦、進(jìn)程和線程的概念:
正在進(jìn)行中的程序被稱為進(jìn)程,負(fù)責(zé)程序運(yùn)行的內(nèi)存分配合是,每一個(gè)進(jìn)程都有自己獨(dú)立的虛擬內(nèi)存空間了罪。
線程是進(jìn)程中一個(gè)獨(dú)立的執(zhí)行路徑,即主線程聪全,主線程有1M的棧區(qū)泊藕,對(duì)于耗時(shí)的執(zhí)行路徑,可以放在子線程(512K棧區(qū))中執(zhí)行荔烧。
新建線程會(huì)消耗內(nèi)存空間和CPU事件吱七,線程太多會(huì)降低系統(tǒng)的運(yùn)行性能,多線程是通過(guò)CPU時(shí)分復(fù)用實(shí)現(xiàn)的鹤竭。
多線程是為了并發(fā)執(zhí)行多項(xiàng)任務(wù)踊餐,不會(huì)提高單個(gè)算法本身的執(zhí)行效率。
2臀稚、同步和異步
同步就是順序執(zhí)行吝岭,執(zhí)行完一個(gè)再執(zhí)行下一個(gè),需要等待吧寺、協(xié)調(diào)運(yùn)行窜管。異步就是彼此獨(dú)立,在等待某事件的過(guò)程中繼續(xù)做自己的事,不需要等待這一事件完成后再工作稚机。線程就是實(shí)現(xiàn)異步的一個(gè)方式幕帆。異步是讓調(diào)用方法的主線程不需要同步等待另一線程的完成,從而可以讓主線程干其它的事情赖条。
異步和多線程并不是一個(gè)同等關(guān)系,異步是最終目的,多線程只是我們實(shí)現(xiàn)異步的一種手段失乾。異步是當(dāng)一個(gè)調(diào)用請(qǐng)求發(fā)送給被調(diào)用者,而調(diào)用者不用等待其結(jié)果的返回而可以做其它的事情。實(shí)現(xiàn)異步可以采用多線程技術(shù)或則交給另外的進(jìn)程來(lái)處理纬乍。
3碱茁、串行和并行
你可以創(chuàng)建任意個(gè)數(shù)的串行隊(duì)列,每個(gè)隊(duì)列依次執(zhí)行添加的任務(wù)仿贬,一個(gè)隊(duì)列同一時(shí)刻只能執(zhí)行一個(gè)任務(wù)(串行)纽竣,但是各個(gè)隊(duì)列之間不影響,可以并發(fā)執(zhí)行。每個(gè)隊(duì)列中的任務(wù)運(yùn)行在一個(gè)由各自串行隊(duì)列維護(hù)的獨(dú)立線程上蜓氨,一個(gè)隊(duì)列中只有一個(gè)線程聋袋。
并行隊(duì)列是不允許自己創(chuàng)建的,系統(tǒng)中存在三個(gè)不同優(yōu)先級(jí)的并行隊(duì)列语盈。并行隊(duì)列依舊按照任務(wù)添加的順序啟動(dòng)任務(wù)舱馅,但是,后一個(gè)任務(wù)無(wú)須等待前一個(gè)任務(wù)執(zhí)行完畢刀荒,而是啟動(dòng)第一個(gè)任務(wù)后代嗤,立即啟動(dòng)下一個(gè)任務(wù)。至于同一時(shí)刻允許同時(shí)運(yùn)行多少個(gè)任務(wù)有系統(tǒng)決定缠借。任務(wù)各自運(yùn)行在并行隊(duì)列為他們提供的獨(dú)立線程上干毅,并行隊(duì)列中同時(shí)運(yùn)行多少個(gè)任務(wù),就必須維護(hù)多少個(gè)線程泼返。
上面講解了關(guān)于GCD的一些基本的概念硝逢,接著我們舉幾個(gè)GCD死鎖的案例:
案例一:當(dāng)同步遇到了串行
NSLog(@"1");//任務(wù)1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");//任務(wù)2
});
NSLog(@"3");//任務(wù)3
控制臺(tái)輸出結(jié)果:
2017-02-23 10:21:00.858 GCDTest[1462:51203] 1
分析:
dispatch_sync是一個(gè)同步線程;
dispatch_get_main_queue()表示運(yùn)行在主線程中的主隊(duì)列绅喉;
任務(wù)二是同步線程的任務(wù)渠鸽。
任務(wù)三需要等待任務(wù)二結(jié)束之后再執(zhí)行。
分析:首先會(huì)執(zhí)行任務(wù)1毫無(wú)疑問(wèn)柴罐,然后程序遇到了同步線程徽缚,任務(wù)三要等待同步線程執(zhí)行完再執(zhí)行,而主線程是一個(gè)特殊的串行對(duì)了革屠。遵循FIFO原則來(lái)執(zhí)行任務(wù)凿试,然后任務(wù)二被加在任務(wù)三的后面要等待任務(wù)三完成才執(zhí)行,這樣就進(jìn)入了互相等待的局面似芝。
案例二:當(dāng)同步遇到了并行
NSLog(@"1");//任務(wù)1
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"2");//任務(wù)2
});
NSLog(@"3");//任務(wù)3
控制臺(tái)輸出結(jié)果:
2017-02-23 10:31:51.647 GCDTest[1488:56267] 1
2017-02-23 10:31:51.648 GCDTest[1488:56267] 2
2017-02-23 10:31:51.648 GCDTest[1488:56267] 3
分析:
毫無(wú)疑問(wèn)首先執(zhí)行任務(wù)一那婉,然后遇到了同步線程,任務(wù)三要等待同步線程執(zhí)行完再執(zhí)行党瓮,然后任務(wù)二被加在了子線程详炬,在子線程執(zhí)行完任務(wù)二然后回到主線程繼續(xù)執(zhí)行任務(wù)三。
案例三:同步異步都有
dispatch_queue_t queue = dispatch_queue_create("com.GCD.serial", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");//任務(wù)1
dispatch_async(queue, ^{
NSLog(@"2");//任務(wù)2
dispatch_sync(queue, ^{
NSLog(@"3");//任務(wù)3
});
NSLog(@"4");//任務(wù)4
});
NSLog(@"5");//任務(wù)5
控制臺(tái)輸出結(jié)果:
2017-02-23 10:47:23.876 GCDTest[1510:63863] 1
2017-02-23 10:47:23.877 GCDTest[1510:63863] 5
2017-02-23 10:47:23.877 GCDTest[1510:63901] 2
//2和5的輸出順序不一定寞奸,3痕寓,4沒(méi)有輸出
分析:
首先,執(zhí)行任務(wù)一蝇闭,毫無(wú)疑問(wèn),然后遇到異步線程操作硬毕,任務(wù)5不必等一步線程操作完再執(zhí)行呻引,所以任務(wù)2和任務(wù)5執(zhí)行先后不一定,二異步操作dispatch_async(queue, ^{})中是串行隊(duì)列操作DISPATCH_QUEUE_SERIAL吐咳,任務(wù)二和任務(wù)四與任務(wù)三依次執(zhí)行逻悠,而任務(wù)三在同步隊(duì)列中元践,任務(wù)四又要等任務(wù)三執(zhí)行完再執(zhí)行,這樣就造成了任務(wù)三和任務(wù)四互相等待的結(jié)局童谒,死鎖单旁。
案例四:異步遇到同步回主線程
NSLog(@"1");//任務(wù)1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");//任務(wù)2
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3");//任務(wù)3
});
NSLog(@"4");//任務(wù)4
});
NSLog(@"5");//任務(wù)5
控制臺(tái)輸出結(jié)果:
2017-02-23 11:08:01.142 GCDTest[1562:72662] 1
2017-02-23 11:08:01.143 GCDTest[1562:72662] 5
2017-02-23 11:08:01.143 GCDTest[1562:72712] 2
2017-02-23 11:08:01.149 GCDTest[1562:72662] 3
2017-02-23 11:08:01.149 GCDTest[1562:72712] 4
分析:
顯然,首先執(zhí)行任務(wù)1饥伊,然后遇到異步全局隊(duì)列象浑,所以異步隊(duì)列和任務(wù)5同時(shí)執(zhí)行,不分先后琅豆,然后異步隊(duì)列中先執(zhí)行任務(wù)2愉豺,遇到同步主隊(duì)列,任務(wù)4要等任務(wù)3完成之后執(zhí)行茫因,而任務(wù)3和任務(wù)4四不在一個(gè)隊(duì)列里蚪拦,所以任務(wù)3不必等待任務(wù)4的完成。所以冻押,整個(gè)案例的執(zhí)行結(jié)果是:15234或者12534驰贷。
案例5:主線程上出現(xiàn)無(wú)限循環(huán):
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");//任務(wù)1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");//任務(wù)2
});
NSLog(@"3");//任務(wù)3
});
NSLog(@"4");//任務(wù)4
while (1) {
}
NSLog(@"5");//任務(wù)5
控制臺(tái)輸出結(jié)果:
2017-02-23 11:23:36.925 GCDTest[1584:78484] 1
2017-02-23 11:23:36.925 GCDTest[1584:78447] 4
//或者1 4
分析:
首先是異步線程,任務(wù)4不用等待任務(wù)1的執(zhí)行洛巢,同時(shí)執(zhí)行括袒,所以任務(wù)1和任務(wù)4肯定能執(zhí)行到,順序不一定狼渊,然后任務(wù)4之后遇到死循環(huán)不再往下執(zhí)行任務(wù)5箱熬,而異步隊(duì)列中任務(wù)2又被加入了串行主隊(duì)列任務(wù)5的后面,所以任務(wù)2也不會(huì)執(zhí)行狈邑,而任務(wù)1在之前之后遇到同步操作城须,任務(wù)3要等待任務(wù)2結(jié)束后再執(zhí)行,所以任務(wù)3也不會(huì)執(zhí)行米苹,因此最后結(jié)果是1 4或者4 1糕伐。
總結(jié):暫時(shí)能找到的死鎖的情況主要就這幾種,如果文章中有不準(zhǔn)確的地方還望指正蘸嘶,或者有可以補(bǔ)充的地方也希望提出來(lái)良瞧,本人小白一枚,互相學(xué)習(xí)训唱。