GCD是iOS開(kāi)發(fā)中非常好用的線程管理方法觉啊,本篇博客會(huì)簡(jiǎn)單介紹GCD隊(duì)列和任務(wù)執(zhí)行方式的概念档泽,以代碼實(shí)驗(yàn)的方式驗(yàn)證GCD各種執(zhí)行方式與線程的關(guān)系。
GCD的介紹
GCD為Grand Central Dispatch的縮寫(xiě)辽旋。
Grand Central Dispatch (GCD)是蘋(píng)果爸爸開(kāi)發(fā)的一個(gè)多核編程的較新的解決方法珠增。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對(duì)稱(chēng)多處理系統(tǒng)。它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)葬馋。在Mac OS X 10.6雪豹中首次推出卖鲤,也可在iOS 4及以上版本使用。
GCD核心思想
GCD的核心思想 —— 任務(wù) 畴嘶, 隊(duì)列
隊(duì)列
隊(duì)列指的是任務(wù)隊(duì)列, 用來(lái)存放任務(wù)的隊(duì)列, 可以說(shuō)隊(duì)列是一種比較特殊的線性表, 采用的是先進(jìn)先出(FIFO)原則, 就像是出高速公路收費(fèi)站一樣, 從最前面一個(gè)開(kāi)始, 排在前面的車(chē)子交了費(fèi)先走, 才到下一個(gè), 這里的隊(duì)列分為兩種,并行隊(duì)列和串行隊(duì)列.
并行隊(duì)列:全名為Concurrent Dispatch Queue, 指的是可以讓多個(gè)任務(wù)同時(shí)執(zhí)行, 如果用到并行隊(duì)列的話(huà), 是會(huì)自動(dòng)開(kāi)啟多個(gè)線程同時(shí)執(zhí)行任務(wù).
串行隊(duì)列:全名Serial Dispatch Queue, 指的是任務(wù)一個(gè)接一個(gè)的執(zhí)行, 完成了前面的那個(gè)就到后面那個(gè), 和我們剛剛舉的收費(fèi)站例子一樣.
使用dispatch_queue_create來(lái)創(chuàng)建一個(gè)隊(duì)列蛋逾,默認(rèn)情況下創(chuàng)建的為串行隊(duì)列。如果需要?jiǎng)?chuàng)建并行隊(duì)列窗悯,需要設(shè)置DISPATCH_QUEUE_CONCURRENT区匣,設(shè)置DISPATCH_QUEUE_SERIAL為串行隊(duì)列。
注意: 并行隊(duì)列只有在異步執(zhí)行(dispatch_async)才有效.
在隊(duì)列中還有還有兩個(gè)會(huì)經(jīng)常使用到的隊(duì)列蒋院。
主隊(duì)列:dispatch_get_main_queue()
主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列亏钩,放在主隊(duì)列中得任務(wù),都會(huì)放到主線程中執(zhí)行欺旧。
全局隊(duì)列:dispatch_get_global_queue(longidentifier,unsignedlongflags)
關(guān)于全局隊(duì)列铸屉,生成一個(gè)全局隊(duì)列需要設(shè)置兩個(gè)參數(shù)。
第一個(gè)參數(shù):用來(lái)設(shè)置該隊(duì)列在整個(gè)任務(wù)執(zhí)行中的優(yōu)先級(jí)別切端。
兩者的對(duì)應(yīng)關(guān)系如下:
- DISPATCH_QUEUE_PRIORITY_HIGH
- DISPATCH_QUEUE_PRIORITY_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW
- DISPATCH_QUEUE_PRIORITY_BACKGROUND
此處值得一提的DISPATCH_QUEUE_PRIORITY_BACKGROUND級(jí)別彻坛,被設(shè)置成后臺(tái)級(jí)別的隊(duì)列,它會(huì)等待所有比它級(jí)別高的隊(duì)列中的任務(wù)執(zhí)行完或CPU空閑的時(shí)候才會(huì)執(zhí)行自己的任務(wù)。例如磁盤(pán)的讀寫(xiě)操作非常耗時(shí)昌屉,如果我們不需要立即獲取到磁盤(pán)的數(shù)據(jù)钙蒙,我們可以把讀寫(xiě)任務(wù)放到后臺(tái)隊(duì)列中,這樣讀寫(xiě)任務(wù)只會(huì)在恰當(dāng)?shù)臅r(shí)候去執(zhí)行而不會(huì)影響需要更改優(yōu)先級(jí)的其他任務(wù)间驮,整個(gè)程序也會(huì)更加有效率躬厌。
任務(wù)
所謂的任務(wù), 就是指我們程序員放在GCD里的操作, 一般是用Block方式進(jìn)行, 這里有兩種執(zhí)行任務(wù)的操作,同步執(zhí)行和異步執(zhí)行, 兩個(gè)的區(qū)別就是在是否開(kāi)啟新線程進(jìn)行操作.
同步執(zhí)行:在GCD里是sync, 不會(huì)開(kāi)啟新線程, 只會(huì)在當(dāng)前線程進(jìn)行操作.
異步執(zhí)行:在GCD里是async, 可以另外開(kāi)啟一個(gè)新的線程執(zhí)行任務(wù).
任務(wù)執(zhí)行分為同步、異步竞帽,隊(duì)列類(lèi)型分為串行扛施、并行,所以搭配如下:
同步 + 串行屹篓;
同步 + 并行疙渣;
異步 + 串行;
異步 + 并行堆巧;
同步 + 串行 的執(zhí)行妄荔,異步 + 并行 的執(zhí)行方式比較顯而易見(jiàn),所以在此處主要針對(duì)同步 + 并行谍肤,異步 + 串行 的任務(wù)做一下實(shí)驗(yàn)驗(yàn)證啦租。
下面我們就開(kāi)始各種歡樂(lè)的實(shí)驗(yàn)吧~
同步 + 并行 操作實(shí)驗(yàn)
執(zhí)行結(jié)果
結(jié)論:叢該實(shí)驗(yàn)中確實(shí)是證實(shí)了并行隊(duì)列在同步執(zhí)行中并不會(huì)開(kāi)辟新的線程,所有的任務(wù)都是在主線程中完成荒揣,并且任務(wù)為一個(gè)一個(gè)的串行執(zhí)行篷角。
異步 + 串行 操作實(shí)驗(yàn)
執(zhí)行結(jié)果
從該實(shí)驗(yàn)操作中,可以看到最先開(kāi)始任務(wù)還是在主線程中執(zhí)行系任,后來(lái)異步執(zhí)行會(huì)開(kāi)辟一條新的線程用來(lái)執(zhí)行隊(duì)列中的任務(wù)内地。但是可以看出開(kāi)辟線程也是會(huì)損耗性能,所以在主線程中打印結(jié)束標(biāo)示的任務(wù)反而先打印出來(lái)赋除。
異步 + 多條串行 實(shí)驗(yàn)操作
執(zhí)行結(jié)果
異步 + 串行 的執(zhí)行結(jié)果得到異步執(zhí)行串行隊(duì)列阱缓,是會(huì)開(kāi)辟新的線程執(zhí)行任務(wù)的,但博主不由想到開(kāi)辟線程數(shù)是否會(huì)和隊(duì)列數(shù)有關(guān)系,通過(guò) 異步 + 多條串行 操作實(shí)驗(yàn)可以得出在異步執(zhí)行情況下,會(huì)為每一條串行隊(duì)列生成一條新的線程執(zhí)行任務(wù)殖属。
異步 + 并行 實(shí)驗(yàn)操作
從 異步 + 并行 的執(zhí)行結(jié)果,對(duì)比 異步 + 串行 執(zhí)行結(jié)果航背,在異步執(zhí)行中一定會(huì)開(kāi)辟新的線程,串行隊(duì)列開(kāi)辟了一條新線程供串行隊(duì)列中的任務(wù)一個(gè)一個(gè)的執(zhí)行棱貌,在并行隊(duì)列中為了確保隊(duì)列中的任務(wù)能盡快的執(zhí)行玖媚,異步執(zhí)行情況下為并行隊(duì)列中的每一個(gè)任務(wù)都開(kāi)辟了一條新的線程,因此可以得出節(jié)省時(shí)間的情況下婚脱,異步 + 并行 的方法是很不錯(cuò)今魔,但從打印結(jié)果可以看出開(kāi)辟線程也會(huì)消耗處理器的資源勺像。
同步 + 多隊(duì)列 實(shí)驗(yàn)操作
同步 + 多隊(duì)列 的結(jié)果證實(shí)了同步情況下,線程數(shù)和隊(duì)列數(shù)無(wú)關(guān)错森,并不會(huì)開(kāi)辟新的線程執(zhí)行任務(wù)吟宦。同步執(zhí)行方式不會(huì)開(kāi)辟新的線程,那么同步執(zhí)行會(huì)在哪一條線程中進(jìn)行任務(wù)執(zhí)行呢涩维?為了得到答案殃姓,博主又進(jìn)行了一次下面的實(shí)驗(yàn)操作。
異步 + 多隊(duì)列 中 任務(wù)進(jìn)行 同步 執(zhí)行 實(shí)驗(yàn)操作
異步 + 多隊(duì)列 中 任務(wù)進(jìn)行 同步 的實(shí)驗(yàn)結(jié)果可以看出來(lái)瓦阐,在隊(duì)列queue1的任務(wù)中我又擦入了一條并行隊(duì)列同步執(zhí)行蜗侈,系統(tǒng)為queue1隊(duì)列開(kāi)辟了線程3來(lái)執(zhí)行任務(wù),因?yàn)橥綀?zhí)行不開(kāi)辟新的線程睡蟋,因此并行隊(duì)列queue3的任務(wù)也直接在queue1的線程3中執(zhí)行了踏幻,因此可以得出同步執(zhí)行不開(kāi)辟線程,同步執(zhí)行的任務(wù)會(huì)在他正在執(zhí)行的線程中執(zhí)行薄湿。
以上做了自己創(chuàng)建的隊(duì)列在同步,異步執(zhí)行中的一些情況偷卧,可以總結(jié)為 異步 + 并行 時(shí)豺瘤,系統(tǒng)會(huì)為每個(gè)任務(wù)開(kāi)創(chuàng)一個(gè)新的線程執(zhí)行,者異步 + 串行 的情況下听诸,隊(duì)列數(shù)決定了線程數(shù)坐求。同步情況下不開(kāi)辟新的線程。
除了自己創(chuàng)建隊(duì)列執(zhí)行任務(wù)之外晌梨,系統(tǒng)也還存在著主隊(duì)列桥嗤、全局隊(duì)列直接供我們使用。
在上文中仔蝌,也提到了全局隊(duì)列dispatch_get_global_queue也屬于并行隊(duì)列泛领,博主做了全局隊(duì)列異步、同步的執(zhí)行后敛惊,執(zhí)行結(jié)果和自創(chuàng)并行隊(duì)列無(wú)異渊鞋,也不由思考全局隊(duì)列和自創(chuàng)并發(fā)隊(duì)列到底有什么卵區(qū)別呢?查閱一些資料后瞧挤,總結(jié)如下:
1锡宋,全局隊(duì)列沒(méi)有名字,但是并發(fā)隊(duì)列有名字特恬。有名字可以便于查看系統(tǒng)日志执俩;
2,全局隊(duì)列是所有應(yīng)用程序共享的癌刽;
3役首,在mrc的時(shí)候尝丐,全局隊(duì)列不用手動(dòng)釋放,但是并發(fā)隊(duì)列需要宋税;
在使用系統(tǒng)的主隊(duì)列時(shí)有一個(gè)值得一提的問(wèn)題:卡線程
以上圖片的寫(xiě)法可以看到摊崭,執(zhí)行結(jié)果在執(zhí)行第一句后就不再執(zhí)行后文了,這種情況叫做卡線程杰赛。大致原因:我們都知道, 同步執(zhí)行是一個(gè)一個(gè)任務(wù)去執(zhí)行的呢簸。但主線程還在執(zhí)行“搞事情的打印”的時(shí)候, 我們又往主線程里塞任務(wù), 這個(gè)時(shí)候就會(huì)出現(xiàn)異常現(xiàn)象乏屯。
優(yōu)化方式
為了避免卡線程的情況根时,那么就先開(kāi)辟一條新的線程,然后再去執(zhí)行主隊(duì)列中的任務(wù)辰晕,這么做打印結(jié)果就正常了蛤迎,并且通過(guò)這個(gè)寫(xiě)法,也得出一個(gè)結(jié)論含友,同步執(zhí)行主隊(duì)列時(shí)替裆,任務(wù)執(zhí)行不會(huì)再保持在原線程里,而是回到了主線程中窘问,主隊(duì)列中的任務(wù)只能在主線程中執(zhí)行辆童。
總結(jié)
通過(guò)以上的各項(xiàng)實(shí)驗(yàn),對(duì)GCD隊(duì)列和任務(wù)在線程中的執(zhí)行方式有了進(jìn)一步了解惠赫,隊(duì)列和任務(wù)只是GCD最普通的用法把鉴,有時(shí)間會(huì)在關(guān)于GCD的實(shí)際使用依然以代碼實(shí)驗(yàn)的方式寫(xiě)一篇博客作為記錄。