GCD是蘋果為多核并行運算提出的方案法瑟,可以更高效的利用CPU。但是更重要的是它使多任務(wù)處理更加高效蟹地。因為它會自動合理的運用多核CPU贯莺。并且自從GCD的內(nèi)存管理也加入ARC之后,它就能自動管理線程的生命周期柱衔。我們只需要把需要的操作(block)告訴它就可以了樊破。
凡說GCD,肯定要說兩個概念
任務(wù)和隊列唆铐,任務(wù)可以同步執(zhí)行和異步執(zhí)行哲戚,存放任務(wù)的隊列分為串行隊列和并行隊列。
一.任務(wù) (block)
就是咱們要執(zhí)行的操作艾岂,GCD中就是block,任務(wù)執(zhí)行在哪個線程由執(zhí)行方式來決定
1.同步執(zhí)行(sync)
會阻塞當(dāng)前任務(wù)所在的線程(同步線程),dispatch_sync(queue, ^(block))把block放到queue中在當(dāng)前線程執(zhí)行顺少。
2.異步執(zhí)行(async)
不會阻塞當(dāng)前任務(wù)所在的線程(異步線程),dispatch_async(queue, ^(block)),如果是串行隊列,則只開一個線程王浴,如果是并行隊列脆炎,則開多個線程。
所以說sync和async決定block在哪個線程中執(zhí)行
二.隊列 (queue)
存放任務(wù)的地方氓辣,負(fù)責(zé)調(diào)度任務(wù)(block)
1.串行隊列(SERIAL)顧名思義就是按順序執(zhí)行隊列中的任務(wù)秒裕,一個任務(wù)完成再執(zhí)行下一個任務(wù)。
主隊列:dispatch_get_main_queue 是一個特殊的串行隊列筛婉,運行在主線程中簇爆,UI相關(guān)操作都要在該隊列中執(zhí)行。
自定義串行隊列:dispatch_queue_create("標(biāo)識", DISPATCH_QUEUE_SERIAL)爽撒,最后一個參數(shù)可以為NULL,默認(rèn)創(chuàng)建的是串行隊列
2.并行隊列(CONCURRENT)就是很多任務(wù)并發(fā)執(zhí)行响蓉,其實GCD中的并行隊列也是根據(jù)FIFO的原則取出任務(wù)硕勿,不同的是取出任務(wù)后GCD會新開一個線程來執(zhí)行任務(wù)。
全局隊列:dispatch_get_global_queue(優(yōu)先級,0); 蘋果公開的全局并行隊列,一共有四個優(yōu)先級
自定義并行隊列:dispatch_queue_create("標(biāo)識", DISPATCH_QUEUE_CONCURRENT)
三.隊列和任務(wù)是怎么執(zhí)行的
GCD的基本概念雖然不多枫甲,但是用起來還是需要理解的深刻一些源武,比如任務(wù)在不同的隊列類別里用不同的方式執(zhí)行會造成什么樣的結(jié)果扼褪,接下來咱們就來一項一項說。
1.串行隊列同步執(zhí)行?
2.串行隊列異步執(zhí)行
執(zhí)行結(jié)果:
1-4為串行隊列同步執(zhí)行
5-6為串行隊列異步執(zhí)行
可以看出來:1-4在當(dāng)前線程一個一個執(zhí)行,5-6新開了一個線程一個一個執(zhí)行(如果串行隊列為主隊列粱栖,則在主線程中執(zhí)行)
3.并行隊列同步執(zhí)行 4.并行隊列異步執(zhí)行
執(zhí)行結(jié)果:
1-4為并行隊列同步執(zhí)行
5-6為并行隊列異步執(zhí)行
可以看出來:1-4在當(dāng)前線程一個一個執(zhí)行话浇,5-6新開了多個線程并發(fā)執(zhí)行
四.實戰(zhàn)場景
理解了任務(wù)、隊列的運行規(guī)則闹究,下面就來看看實際項目中在何種場景下運用GCD
1.運用dispatch_async來避免一些耗時的任務(wù)阻塞主線程(卡死界面).
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
? ? ? ? NSLog(@"耗時的操作讀取數(shù)據(jù)庫幔崖,網(wǎng)絡(luò)操作等");
? ? ? ? dispatch_sync(dispatch_get_main_queue(), ^{
? ? ? ? ? ? ? NSLog(@"操作完成_刷新UI在:%@",i,[NSThread currentThread]);
? ? ? ? });
});
這里可能有人會問:回到主線程的操作,dispatch_sync和dispatch_async有什么區(qū)別渣淤?
大家可以想想這個問題赏寇,如果理解了前面說的隊列和任務(wù),你應(yīng)該知道結(jié)果价认,文章后面還會提到嗅定。
2.運用dispatch_apply進(jìn)行快速迭代.
如果是串行隊列,dispatch_apply和for是一樣的用踩,
如果運用在并發(fā)隊列渠退,那么dispatch_apply就會并發(fā)的執(zhí)行任務(wù)(block),大大的提高遍歷速度脐彩。
> 兩種方法各循環(huán)100000次碎乃,for耗時32.9秒,apply耗時19.4秒
> 效率提高了40%多丁屎。
有人說可以在for循環(huán)里面用dispatch_async開啟子線程執(zhí)行任務(wù)荠锭,的確可以,但是如果開啟的子線程多了晨川,很有可能線程爆炸造成死鎖等情況证九。而GCD會管理并發(fā),所以apply還能避免線程爆炸的問題共虑,實在是居家旅行愧怜、殺人滅口,必備良藥妈拌。
3.dispatch_group 調(diào)度組
開發(fā)中我們經(jīng)常會遇到這樣的需求:同時調(diào)用多個接口拥坛,所有接口返回后再刷新界面。如果不用調(diào)度組的話尘分,應(yīng)該怎么做猜惋?大部分人都會把這幾個接口串行起來,等最后一個接口返回再執(zhí)行刷新界面的方法培愁,這樣做的話如果其中一個接口返回失敗著摔,那么整個頁面就無法刷新了。還有的同學(xué)每個接口回來都刷新一次界面定续,這樣會造成頁面閃爍谍咆,嚴(yán)重的話禾锤,并發(fā)調(diào)用同時回來結(jié)果操作同一數(shù)據(jù)源,還有可能造成崩潰摹察。
這時無疑用GCD調(diào)度組是最好的解決方法恩掷。調(diào)度組會在組里所有的任務(wù)執(zhí)行完畢后發(fā)送一個通知告訴我們,組內(nèi)的任務(wù)全部執(zhí)行完畢供嚎。
接收通知的方式有兩種黄娘,
同步執(zhí)行的dispatch_block_wait,會阻塞當(dāng)前線程并等待之前的任務(wù)全部執(zhí)行完或超時再執(zhí)行wait中隊列里的任務(wù)
異步執(zhí)行的dispatch_group_notify查坪,作用和wait一樣寸宏,但是是異步執(zhí)行的,所以不會阻塞當(dāng)前線程
dispatch_group_enter和dispatch_group_leave可以手動管理group中的任務(wù)計數(shù)偿曙,enter為+1氮凝,leave為-1,當(dāng)計數(shù)為0時望忆,才會進(jìn)入wait或notify中的任務(wù)
具體執(zhí)行看代碼:這里用after延遲提交任務(wù)(block)的做法來模擬調(diào)用接口時的情景罩阵。
以上代碼中創(chuàng)建了一個調(diào)度組group,并且指定調(diào)度組中的任務(wù)在全局隊列中運行启摄。在執(zhí)行結(jié)果中可以看到稿壁,全局隊列中先執(zhí)行了任務(wù)2和任務(wù)1(強(qiáng)制停止了1秒),因為wait會在當(dāng)前線程等待任務(wù)1歉备,2都完成之后才執(zhí)行傅是,所以wait執(zhí)行完后再執(zhí)行任務(wù)3,4蕾羊,這里用了手動計數(shù)的方法控制任務(wù)計數(shù)喧笔,當(dāng)3,4都執(zhí)行完后龟再,計數(shù)歸0书闸,最后計入notify的任務(wù),當(dāng)我們需要同時并發(fā)執(zhí)行多個接口之后再執(zhí)行某項操作時利凑,調(diào)度組非常實用浆劲。
4.GCD中容易遇到的死鎖問題
GCD的任務(wù)和隊列都是在線程中運行的,所以頻繁的操作線程如果不注意會很容易造成死鎖問題哀澈,所謂死鎖牌借,就是指兩個線程互相等待對方完成某項操作,導(dǎo)致線程卡死割按,當(dāng)然歸根結(jié)底是對任務(wù)和隊列的運行方式理解的還不夠徹底走哺,下面列舉一些容易造成死鎖的現(xiàn)象,coding中要格外注意哲虾。
案例1:最簡單的死鎖現(xiàn)象
控制臺:
1.
我們來分析一下堆棧信息丙躏,根據(jù)FIFO的原則,任務(wù)1 - 同步線程-任務(wù)3-任務(wù)2
因為同步線程阻塞了主線程束凑,所以任務(wù)2等待任務(wù)3執(zhí)行晒旅,任務(wù)3又等待任務(wù)2,造成死鎖汪诉,程序卡死废恋。
這時如果我們再稍微復(fù)雜一些呢
案例2:串行隊列中同步執(zhí)行一個并行隊列中的任務(wù)
控制臺:
1
2
3
這個比較好理解扒寄,在主線程中打印1鱼鼓,這時同步線程在全局隊列里面執(zhí)行2,不會像1一樣把任務(wù)2直接加在主隊列隊尾该编,所以不存在2迄本,3相互等待的情況,而是同步線程阻塞了主線程后等待任務(wù)2執(zhí)行完畢后课竣,順序執(zhí)行3.
案例3:異步線程執(zhí)行后回到同步線程執(zhí)行
是不是很眼熟,這就是4.實戰(zhàn)場景中的第一個案例,我們當(dāng)然不是要演示這個嘉赎,繼續(xù)看下圖兩張圖
控制臺:
1
8
2
3
4
5
6
7
控制臺:
1
8
2
7
3
4
5
6
控制臺:
分析:輸出0后于樟,就是異步線程公条,所以任務(wù)4不用等待,1迂曲,4執(zhí)行順序不一定靶橱。任務(wù)4完成后,接著是一個阻塞線程的同步任務(wù)5路捧,但是加入到全局隊列的異步線程不受影響关霸,繼續(xù)執(zhí)行1后面的同步線程中的任務(wù)2,并且任務(wù)3需要等任務(wù)2完成后才能執(zhí)行鬓长。但是這時主線程已經(jīng)被一個同步線程的任務(wù)5谒拴,和6死鎖,所以任務(wù)2也無法執(zhí)行涉波。
總結(jié)
GCD除了上面說的這些英上,只要理解了他的運行方式,可以靈活的組合出很多用法啤覆,當(dāng)然這篇文章只是說了GCD的鳳毛麟角苍日,NSOperation和NSOperationQueue還沒有提到,先理解了GCD窗声,咱們下次說NSOperation和NsOperationQueue相恃。