CGD簡(jiǎn)介
什么是GCD
- GCD的全稱是Grand Central Dispatch,可以把它翻譯為"牛逼的中樞調(diào)度器"。??
- 純C語言,提供了很多強(qiáng)大的函數(shù)
GCD的優(yōu)勢(shì)
- GCD是蘋果公司為解決多核的并行運(yùn)算而提出的解決方案
- CGD會(huì)自動(dòng)利用更多的CPU內(nèi)容(如雙核、四核...)
- GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
- 程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
GCD中的概念和使用步驟
2個(gè)核心概念
任務(wù):
執(zhí)行的什么操作,用block代碼塊的形式創(chuàng)建
隊(duì)列:
是用來陳放任務(wù)的載體,負(fù)責(zé)調(diào)度任務(wù);隊(duì)列有兩種類型:
- 串行隊(duì)列:一個(gè)接一個(gè)的調(diào)度任務(wù) (
DISPATCH_QUEUE_SERIAL
、dispatch_get_main_queue()
) - 并行隊(duì)列:可以同時(shí)調(diào)度多個(gè)任務(wù) (
DISPATCH_QUEUE_CONCURRENT
、dispatch_get_global_queue(0,0)
)
使用步驟(2步)
定制任務(wù)
確定想要執(zhí)行的任務(wù)
將任務(wù)添加到隊(duì)列中
- GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出,放到對(duì)應(yīng)的線程中執(zhí)行
- 任務(wù)的取出遵循隊(duì)列的FIFO原則:先進(jìn)先出
在這個(gè)過程中,我們需要做的就是:將任務(wù)以指定的方式(同步/異步)添加到隊(duì)列中,隊(duì)列就會(huì)按照我們指定的方式調(diào)度任務(wù)。
- 同步:一個(gè)任務(wù)沒有結(jié)束,就不會(huì)執(zhí)行下一個(gè)任務(wù)
- 異步:不用等當(dāng)前任務(wù)執(zhí)行完畢,就會(huì)去線程池開辟一個(gè)子線程,執(zhí)行下一個(gè)任務(wù)
- 線程池:在GCD的底層有一個(gè)線程池,負(fù)責(zé)創(chuàng)建線程、分配線程、銷毀線程
我的理解
如上圖,GCD主要由任務(wù)、隊(duì)列、執(zhí)行方式三個(gè)部分組成,又由于隊(duì)列類型(串行/并行)、執(zhí)行方式(同步/異步)的不同,可以兩兩組合,從而出現(xiàn)四種情況,后面會(huì)一一通過代碼來實(shí)現(xiàn)。這里主要說明的是:
對(duì)于串行隊(duì)列
當(dāng)任務(wù)執(zhí)行時(shí),必須要等當(dāng)前任務(wù)執(zhí)行完成后才能去隊(duì)列中拿下一個(gè)任務(wù),所以不管是同步執(zhí)行,還是異步執(zhí)行,都是按照順序執(zhí)行的對(duì)于并行隊(duì)列
當(dāng)任務(wù)執(zhí)行時(shí),不用等當(dāng)前任務(wù)執(zhí)行完畢就可以去隊(duì)列拿下一個(gè)任務(wù),此時(shí)若是:- 同步執(zhí)行
由于只有同步執(zhí)行不會(huì)去現(xiàn)成池拿新的線程,即使你拿到多個(gè)任務(wù),也只能在當(dāng)前線程排隊(duì)等待,等前一個(gè)任務(wù)執(zhí)行完畢后,才能執(zhí)行下一個(gè),所以還是按照順序執(zhí)行
- 同步執(zhí)行
- 異步執(zhí)行
如果在并行隊(duì)列異步執(zhí)行任務(wù)的話,首先會(huì)去隊(duì)列中拿到多個(gè)任務(wù),并且會(huì)去線程池中獲取子線程,所以會(huì)出現(xiàn)多個(gè)任務(wù)并發(fā)執(zhí)行的情況,這時(shí)候任務(wù)的執(zhí)行順序是不確定的,而我們用GCD大部分的場(chǎng)景都是這種情況
- 異步執(zhí)行
下面,通過代碼,來依次驗(yàn)證這些情況。
GCD實(shí)戰(zhàn)
創(chuàng)建隊(duì)列
所有的隊(duì)列都是dispatch_queue_t
類型,常用創(chuàng)建隊(duì)列通常有三種方法,其中有兩種是系統(tǒng)提供的:
主隊(duì)列
通過dispatch_get_main_queue()
方法返回一個(gè)dispatch_queue_t
對(duì)象,這是一個(gè)串行隊(duì)列,即該隊(duì)列中的所有任務(wù)都是順序執(zhí)行的。全局隊(duì)列
這個(gè)隊(duì)列通過dispatch_get_global_queue(long identifier, unsigned long flags)
函數(shù)獲取,對(duì)于這個(gè)函數(shù)有兩個(gè)參數(shù):-
identifier
這個(gè)參數(shù)是表示一個(gè)優(yōu)先級(jí)的,但是在iOS 8之前和之后其參數(shù)類型不太一樣
iOS 8
及之后版本表示的含義是:服務(wù)質(zhì)量,其類型如下:
-
QOS_CLASS_USER_INTERACTIVE 用戶交互(希望線程快速被執(zhí)行,不要用好使的操作)
QOS_CLASS_USER_INITIATED 用戶需要的(同樣不要使用耗時(shí)操作)
QOS_CLASS_DEFAULT 默認(rèn)的(給系統(tǒng)來重置隊(duì)列的)
QOS_CLASS_UTILITY 使用工具(用來做耗時(shí)操作)
QOS_CLASS_BACKGROUND 后臺(tái)
QOS_CLASS_UNSPECIFIED 沒有指定優(yōu)先級(jí)
而在iOS 8
之前版本表示的含義是:調(diào)度的優(yōu)先級(jí),其類型如下:
DISPATCH_QUEUE_PRIORITY_HIGH 2 高優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認(rèn)優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后臺(tái)優(yōu)先級(jí)
提示
不要選擇BACKGROUND
優(yōu)先級(jí),服務(wù)質(zhì)量,因?yàn)榫€程執(zhí)行會(huì)慢到令人發(fā)指!!!體現(xiàn)在任務(wù)調(diào)度次數(shù)少,執(zhí)行時(shí)間短。通常基本都使用默認(rèn)的,填入0
-
flags
: 是一個(gè)預(yù)留參數(shù),同常填0
-
- 自己創(chuàng)建一個(gè)隊(duì)列
GCD提供了一個(gè)可以自己創(chuàng)建隊(duì)列的函數(shù):dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)
函數(shù),使用此函數(shù)可以創(chuàng)建一個(gè)自定義的隊(duì)列。 -
label
參數(shù): 是一個(gè)隊(duì)列標(biāo)簽,即隊(duì)列名,通常給一個(gè)字符串
-
-
attr
參數(shù): 是隊(duì)列屬性;系統(tǒng)提供了兩個(gè)宏:DISPATCH_QUEUE_SERIAL
或者NULL
因?yàn)檫@個(gè)宏就是NULL
,表示串行隊(duì)列;DISPATCH_QUEUE_CONCURRENT
:表示并行隊(duì)列。
-
添加任務(wù)
添加任務(wù):將任務(wù)以指定的執(zhí)行方式添加到隊(duì)列中去;它的方式有兩種:
- 同步執(zhí)行:
即只會(huì)在當(dāng)前線程中執(zhí)行任務(wù),無法去線程池中獲取新的線程
/**
*queue: 添加到的隊(duì)列
*block:就放我們需要執(zhí)行的任務(wù)
*/
dispatch_sync(dispatch_queue_t _Nonnull queue, ^{
//you todo
});
- 異步執(zhí)行:
即可以去線程池中獲取新的線程,其參數(shù)含義和同步執(zhí)行的相同,只是函數(shù)名換成了dispatch_async
dispatch_async(dispatch_queue_t _Nonnull queue, ^{
//you todo
})
介紹完了基本函數(shù)語法,下面來用代碼實(shí)現(xiàn)上述所說的4中情況。
串行隊(duì)列同步執(zhí)行
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self gcdBase1];
}
- (void)gcdBase1 {
dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_SERIAL);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"%ld ------ %@", (long)i, [NSThread currentThread]);
});
}
}
我在touchesBegan
方法中,串行隊(duì)列中以同步執(zhí)行的方法添加了10個(gè)NSLog
任務(wù),當(dāng)點(diǎn)擊屏幕時(shí),控制臺(tái)獲得以下輸出:
2017-01-10 21:54:58.609 GCD-01[13124:1295897] 0 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.609 GCD-01[13124:1295897] 1 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.609 GCD-01[13124:1295897] 2 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.610 GCD-01[13124:1295897] 3 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.610 GCD-01[13124:1295897] 4 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.610 GCD-01[13124:1295897] 5 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.610 GCD-01[13124:1295897] 6 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.611 GCD-01[13124:1295897] 7 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.611 GCD-01[13124:1295897] 8 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.611 GCD-01[13124:1295897] 9 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
由結(jié)果可以看出,不會(huì)開啟線程,并且任務(wù)是順序執(zhí)行的,符合預(yù)期,并且無論你點(diǎn)擊多少次,結(jié)果都不會(huì)改變。
串行隊(duì)列異步執(zhí)行
這次我在touchesBegan
方法中執(zhí)行以下任務(wù),在一個(gè)串行隊(duì)列中,添加10個(gè)異步任務(wù),并且在任務(wù)添加后,打印一句I am here
,猜猜這次的打印結(jié)果會(huì)是怎么樣的?????
- (void)gcdBase2 {
dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_SERIAL);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"%ld ------ %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"I am here");
}
在模擬器中點(diǎn)擊屏幕,獲得以下結(jié)果:
2017-01-10 22:06:01.703 GCD-01[13385:1322583] I am here <NSThread: 0x600000066a40>{number = 1, name = main}
2017-01-10 22:06:01.703 GCD-01[13385:1322734] 0 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.703 GCD-01[13385:1322734] 1 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.703 GCD-01[13385:1322734] 2 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.703 GCD-01[13385:1322734] 3 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.704 GCD-01[13385:1322734] 4 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.704 GCD-01[13385:1322734] 5 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.704 GCD-01[13385:1322734] 6 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.705 GCD-01[13385:1322734] 7 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.705 GCD-01[13385:1322734] 8 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.705 GCD-01[13385:1322734] 9 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
由以上結(jié)果可以看出,異步執(zhí)行,會(huì)創(chuàng)建新的線程去執(zhí)行任務(wù),在串行隊(duì)列中,由于每次只能拿一個(gè)任務(wù),所以任務(wù)是按照FIFO
順序執(zhí)行的。這里的I am here
位置不一定一直處于第一位,但是肯定是靠前的。但是我試了很多次,都是第一位,尷尬。。。
并行隊(duì)列同步執(zhí)行
我在touchesBegan
方法中執(zhí)行以下任務(wù);將隊(duì)列類型換成DISPATCH_QUEUE_CONCURRENT
,即并行隊(duì)列,然后添加同步任務(wù)
- (void)gcdBase3 {
dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"%ld ------ %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"I am here %@", [NSThread currentThread]);
}
點(diǎn)擊屏幕獲取打印:
2017-01-10 22:12:34.551 GCD-01[13537:1337073] 0 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.551 GCD-01[13537:1337073] 1 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.551 GCD-01[13537:1337073] 2 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.552 GCD-01[13537:1337073] 3 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.552 GCD-01[13537:1337073] 4 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.552 GCD-01[13537:1337073] 5 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.552 GCD-01[13537:1337073] 6 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.553 GCD-01[13537:1337073] 7 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.553 GCD-01[13537:1337073] 8 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.553 GCD-01[13537:1337073] 9 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.553 GCD-01[13537:1337073] I am here <NSThread: 0x6180000764c0>{number = 1, name = main}
結(jié)果:由打印信息可以看出,同步任務(wù)不會(huì)創(chuàng)建新的線程,由于只有一個(gè)線程,即使是并行隊(duì)列可以同時(shí)獲取多個(gè)任務(wù),最終也會(huì)按照順序執(zhí)行,因?yàn)橹挥幸粭l線程。
并行隊(duì)列異步執(zhí)行
- (void)gcdBase4 {
dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"%ld ------ %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"I am here %@", [NSThread currentThread]);
}
這次我多次點(diǎn)擊屏幕時(shí),獲得以下輸出:
2017-01-10 22:18:15.768 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:15.768 GCD-01[13664:1350805] 0 ------ <NSThread: 0x618000070700>{number = 3, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351252] 1 ------ <NSThread: 0x61800006bf80>{number = 4, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351253] 2 ------ <NSThread: 0x610000073880>{number = 5, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351254] 3 ------ <NSThread: 0x61800006afc0>{number = 6, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351255] 4 ------ <NSThread: 0x6000000725c0>{number = 7, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351256] 5 ------ <NSThread: 0x61800006a2c0>{number = 8, name = (null)}
2017-01-10 22:18:15.769 GCD-01[13664:1351257] 6 ------ <NSThread: 0x610000073540>{number = 9, name = (null)}
2017-01-10 22:18:15.769 GCD-01[13664:1351258] 7 ------ <NSThread: 0x618000070480>{number = 10, name = (null)}
2017-01-10 22:18:15.769 GCD-01[13664:1350805] 8 ------ <NSThread: 0x618000070700>{number = 3, name = (null)}
2017-01-10 22:18:15.769 GCD-01[13664:1351252] 9 ------ <NSThread: 0x61800006bf80>{number = 4, name = (null)}
2017-01-10 22:18:24.114 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:24.115 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351259] 0 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351638] 2 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351639] 3 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351641] 4 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351643] 5 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351640] 7 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351644] 6 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351637] 8 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351647] 9 ------ <NSThread: 0x610000073540>{number = 19, name = (null)}
2017-01-10 22:18:26.446 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:26.446 GCD-01[13664:1351647] 0 ------ <NSThread: 0x610000073540>{number = 19, name = (null)}
2017-01-10 22:18:26.446 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:26.446 GCD-01[13664:1351644] 2 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
2017-01-10 22:18:26.446 GCD-01[13664:1351640] 3 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351643] 4 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351641] 5 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351639] 6 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351638] 7 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351259] 8 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351646] 9 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351646] 0 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:26.598 GCD-01[13664:1351259] 1 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351638] 2 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351639] 3 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351641] 4 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351643] 5 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351640] 6 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351644] 7 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351637] 8 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:26.599 GCD-01[13664:1351649] 9 ------ <NSThread: 0x61800006f680>{number = 21, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351649] 0 ------ <NSThread: 0x61800006f680>{number = 21, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351644] 2 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:26.734 GCD-01[13664:1351640] 3 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351643] 4 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351641] 5 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351639] 6 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351638] 7 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351259] 8 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
2017-01-10 22:18:26.735 GCD-01[13664:1351646] 9 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}
結(jié)果分析: 由多次點(diǎn)擊結(jié)果可以確定,異步任務(wù),開啟了新線程,并發(fā)執(zhí)行能夠拿到多個(gè)任務(wù),所以上面會(huì)出現(xiàn)任務(wù)執(zhí)行順序不一致,并且I am here
的位置,也可以確定主線程沒有被影響。
一個(gè)模擬項(xiàng)目中的同步任務(wù)
在這里我模擬一個(gè)實(shí)際開發(fā)流程:比如在一個(gè)讀書的APP中,通常會(huì)出現(xiàn)付費(fèi)下載的情況,當(dāng)用戶在游客模式去瀏覽,發(fā)現(xiàn)到自己喜歡的書籍時(shí),去點(diǎn)擊下載,這時(shí)候就需要走 登錄 -> 支付 -> 下載,這就是一個(gè)同步任務(wù),這里有很多網(wǎng)絡(luò)請(qǐng)求,屬于耗時(shí)操作,通常我們都在子線程中去完成。在這個(gè)任務(wù)中,我們將設(shè)置一下流程,用戶必須先登錄,然后同時(shí)執(zhí)行支付和下載任務(wù)。對(duì)此,實(shí)現(xiàn)以下代碼:
- (void)gcdSyncTask {
//創(chuàng)建一個(gè)并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
//同步添加 用戶登錄 任務(wù)
dispatch_sync(queue, ^{
NSLog(@"用戶登錄");
});
//異步添加 支付任務(wù) 可以在子線程中執(zhí)行
dispatch_async(queue, ^{
NSLog(@"支付任務(wù)");
});
//異步添加 下載任務(wù) 可以在子線程中執(zhí)行
dispatch_async(queue, ^{
NSLog(@"下載任務(wù)");
});
}
當(dāng)點(diǎn)擊屏幕獲得以下打印:經(jīng)過多次點(diǎn)擊都可以發(fā)現(xiàn),用戶登錄永遠(yuǎn)都先執(zhí)行的,只有用戶登錄完成后,才會(huì)去執(zhí)行支付和下載任務(wù)。實(shí)際情況,任務(wù)的執(zhí)行會(huì)更加耗時(shí),那么支付和下載的完成順序也就會(huì)不確定
2017-01-10 22:50:53.816 GCD-01[14340:1423863] 用戶登錄
2017-01-10 22:50:53.816 GCD-01[14340:1423979] 支付任務(wù)
2017-01-10 22:50:53.816 GCD-01[14340:1423977] 下載任務(wù)
對(duì)模擬項(xiàng)目升級(jí)
在之前的項(xiàng)目中,所以的點(diǎn)擊添加任務(wù)都是在主線程中添加的,下面模擬一下在,子線程中添加上述的同步任務(wù)會(huì)出現(xiàn)什么情況:
- (void)gcdStrongSyncTask {
//創(chuàng)建一個(gè)并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
//這里講之前的任務(wù) 放到task代碼快中
void (^task)() = ^{
for (NSInteger i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"%ld ------ %@", (long)i, [NSThread currentThread]);
});
}
//同步添加 用戶登錄 任務(wù)
dispatch_sync(queue, ^{
NSLog(@"用戶登錄 %@", [NSThread currentThread]);
});
//異步添加 支付任務(wù) 可以在子線程中執(zhí)行
dispatch_async(queue, ^{
NSLog(@"支付任務(wù) %@", [NSThread currentThread]);
});
//異步添加 下載任務(wù) 可以在子線程中執(zhí)行
dispatch_async(queue, ^{
NSLog(@"下載任務(wù) %@", [NSThread currentThread]);
});
};
//以異步任務(wù)的方式,將task在子線程中去執(zhí)行,從而完成在子線程中去執(zhí)行之前的任務(wù)
dispatch_async(queue, task);
NSLog(@" I come here %@", [NSThread currentThread]);
}
以下結(jié)果是我多次點(diǎn)擊之后得到的,我選取其中有代表性的一段,可以發(fā)現(xiàn)兩點(diǎn):
- 將之前的任務(wù)放在block中,從新以異步任務(wù)方式添加到隊(duì)列中,就會(huì)在子線程中執(zhí)行
- "下載任務(wù)"會(huì)在"支付任務(wù)之前完成"
2017-01-10 23:05:00.053 GCD-01[14704:1458015] I come here <NSThread: 0x60000006e440>{number = 1, name = main}
2017-01-10 23:05:00.053 GCD-01[14704:1458400] 0 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.054 GCD-01[14704:1458400] 1 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.054 GCD-01[14704:1458400] 2 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.055 GCD-01[14704:1458400] 3 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.055 GCD-01[14704:1458400] 4 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.055 GCD-01[14704:1458400] 5 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.056 GCD-01[14704:1458400] 6 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.056 GCD-01[14704:1458400] 7 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.056 GCD-01[14704:1458400] 8 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.056 GCD-01[14704:1458400] 9 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.057 GCD-01[14704:1458400] 用戶登錄 <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.057 GCD-01[14704:1458400] 支付任務(wù) <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.057 GCD-01[14704:1458089] 下載任務(wù) <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.573 GCD-01[14704:1458015] I come here <NSThread: 0x60000006e440>{number = 1, name = main}
2017-01-10 23:05:00.573 GCD-01[14704:1458089] 0 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.574 GCD-01[14704:1458089] 1 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.574 GCD-01[14704:1458089] 2 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.574 GCD-01[14704:1458089] 3 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.575 GCD-01[14704:1458089] 4 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.575 GCD-01[14704:1458089] 5 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.575 GCD-01[14704:1458089] 6 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.575 GCD-01[14704:1458089] 7 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.576 GCD-01[14704:1458089] 8 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.576 GCD-01[14704:1458089] 9 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.576 GCD-01[14704:1458089] 用戶登錄 <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.577 GCD-01[14704:1458400] 下載任務(wù) <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.577 GCD-01[14704:1458089] 支付任務(wù) <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
總結(jié)
對(duì)于GCD的基本使用,注意以下三點(diǎn):
- 串行隊(duì)列每次只能執(zhí)行一個(gè)任務(wù),不管是同步還是異步,因?yàn)殛?duì)列本身只會(huì)等當(dāng)前任務(wù)完成后才能去取出下一個(gè)任務(wù),入口限制;
- 并行隊(duì)列同步執(zhí)行時(shí):即使能從隊(duì)列中獲取多個(gè)任務(wù),也只能等待,因?yàn)橥綀?zhí)行,不會(huì)創(chuàng)建子線程,所有任務(wù)都只能在當(dāng)前線程中完成,而當(dāng)前線程,只能等當(dāng)前任務(wù)執(zhí)行完,才能去執(zhí)行下一個(gè)任務(wù),出口限制;
- 并行隊(duì)列異步執(zhí)行:并行隊(duì)列可以獲取多個(gè)任務(wù),異步執(zhí)行可以獲取多個(gè)線程,所以會(huì)出現(xiàn)并發(fā)執(zhí)行,沒有限制。