一.GCD簡介
GCD(Grand Central Dispatch) 是iOS系統(tǒng)一套多線程操作API高镐。字面意思既是宏觀全局派發(fā)溉旋。相比于其他多線程API,GCD操作簡單,功能強(qiáng)大嫉髓,且會自動充分利用系統(tǒng)硬件資源观腊。操作簡單是因?yàn)槲覀儫o需管理線程邑闲,只需定義想要執(zhí)行的任務(wù),然后添加到適當(dāng)?shù)恼{(diào)度隊(duì)列(dispatch queue)。GCD會負(fù)責(zé)創(chuàng)建線程和調(diào)度任務(wù)梧油,由系統(tǒng)直接提供線程管理苫耸。功能強(qiáng)大是指系統(tǒng)封裝好了很多C函數(shù),調(diào)用這些C函數(shù)就可以實(shí)現(xiàn)線程操作中的絕大多數(shù)功能儡陨。
GCD里很重要的一個(gè)概念是隊(duì)列queue,隊(duì)列是一種里面的元素按先進(jìn)先出規(guī)則的線性結(jié)構(gòu)褪子。我們可以把任務(wù)(往往都是block)往隊(duì)列里添加,系統(tǒng)會自動去隊(duì)里里取出任務(wù)骗村,然后根據(jù)當(dāng)前的隊(duì)列是并行隊(duì)列 還是串行 隊(duì)列褐筛,響應(yīng)的去分配到線程執(zhí)行。
隊(duì)列相當(dāng)于我們與系統(tǒng)交互的媒介叙身,我們把任務(wù)往隊(duì)列里扔渔扎,系統(tǒng)去隊(duì)列里取出任務(wù),開辟線程信轿,分配任務(wù)晃痴。隊(duì)列有并行隊(duì)列與串行隊(duì)列之分,不同隊(duì)列有不同表現(xiàn)形式财忽。
二.并行串行倘核、同步異步介紹
學(xué)習(xí)GCD,首先要明白兩組概念,并行和串行即彪、同步與異步
并行和串行比較好理解紧唱,按字面意思,并行指的并發(fā)執(zhí)行隶校,也就是開辟多個(gè)線程同時(shí)去執(zhí)行隊(duì)列里任務(wù)漏益,串行的表現(xiàn)形式就是添加到隊(duì)列里的任務(wù)會同時(shí)執(zhí)行,因此后添加進(jìn)隊(duì)列的任務(wù)可能會先完成深胳,而先添加進(jìn)隊(duì)列的任務(wù)不一定就先完成绰疤,串行則是指隊(duì)里中的任務(wù)嚴(yán)格按照添加進(jìn)隊(duì)列的順序先后執(zhí)行。
同步異步主要是指是否阻塞當(dāng)前代碼執(zhí)行舞终, 等待隊(duì)列里的任務(wù)都執(zhí)行完才可以繼續(xù)執(zhí)行轻庆。同步執(zhí)行會先執(zhí)行隊(duì)列里的任務(wù),執(zhí)行完后等待dispatch_sync函數(shù)返回才能繼續(xù)執(zhí)行下面操作敛劝,異步執(zhí)行不需要等待隊(duì)列任務(wù)執(zhí)行完成余爆,將任務(wù)添加到隊(duì)列后,dispatch_sync函數(shù)馬上返回,繼續(xù)執(zhí)行下面操作夸盟,同時(shí)另外開辟新線程去執(zhí)行隊(duì)列里的任務(wù)蛾方。同步執(zhí)行時(shí)候 系統(tǒng)會盡量做到不開辟新線程,只在當(dāng)前線程里執(zhí)行隊(duì)列中的任務(wù)。
三.GCD使用
使用GCD很簡單转捕,兩步即可完成作岖,
1.首先創(chuàng)建隊(duì)列,
2.然后將任務(wù)放到隊(duì)列里五芝。
1.創(chuàng)建隊(duì)列
創(chuàng)建隊(duì)列 可以使用dispatch_queue_create來創(chuàng)建隊(duì)列痘儡,需要傳入兩個(gè)參數(shù),第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識符枢步,用于唯一標(biāo)志隊(duì)列沉删,可以為空;第二個(gè)參數(shù)用來識別是串行隊(duì)列還是并行隊(duì)列醉途。DISPATCH_QUEUE_SERIAL表示串行隊(duì)列矾瑰,DISPATCH_QUEUE_CONCURRENT表示并行隊(duì)列。默認(rèn)NULL表示串行隊(duì)列隘擎。
// 串行隊(duì)列的創(chuàng)建方法 dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 并行隊(duì)列的創(chuàng)建方法 dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT
系統(tǒng)為了方便開發(fā)使用殴穴,已經(jīng)定義好了一個(gè)全局并發(fā)隊(duì)列,dispatch_get_global_queue,創(chuàng)建時(shí)候有兩個(gè)參數(shù)货葬,第一個(gè)參數(shù)表示隊(duì)列優(yōu)先級采幌,一般用默認(rèn)優(yōu)先級DISPATCH_QUEUE_PRIORITY_DEFAULT。第二個(gè)參數(shù)由系統(tǒng)預(yù)留做以后使用震桶,現(xiàn)在沒用休傍,可以先用0表示。
2.向隊(duì)列里分配任務(wù)蹲姐,任務(wù)以block的形式被添加到隊(duì)列中
使用同步或者異步兩種方式分配任務(wù)
// 同步執(zhí)行任務(wù)創(chuàng)建方法 dispatch_sync(queue, ^{ NSLog(@"%@",[NSThread currentThread]); // 所需要執(zhí)行的任務(wù) });
// 異步執(zhí)行任務(wù)創(chuàng)建方法 dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); // 所需要執(zhí)行的任務(wù) });
其中磨取,任務(wù)既可以是臨時(shí)聲明的block,也可以是提前定義好的block柴墩。
這個(gè)block是dispatch_async和dispatch_sync的參數(shù)
@interface ViewController ()
@property (nonatomic, copy) dispatch_block_t block;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//定義隊(duì)列里需要執(zhí)行的block,即任務(wù)
_block = ^(){
for (int i = 0; i < 10 ; i++) {
NSLog(@"%d-------%@",i,[NSThread currentThread]);
}
};
//創(chuàng)建隊(duì)列 并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
//異步執(zhí)行創(chuàng)建好的隊(duì)列
dispatch_async(queue,_block);
NSLog(@"等不等我");
}
運(yùn)行結(jié)果如下
2017-09-13 00:12:13.995 GCD[1148:97167] 等不等我
2017-09-13 00:12:13.995 GCD[1148:97384] 0-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:13.997 GCD[1148:97384] 1-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:13.998 GCD[1148:97384] 2-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:13.998 GCD[1148:97384] 3-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:13.999 GCD[1148:97384] 4-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:13.999 GCD[1148:97384] 5-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:13.999 GCD[1148:97384] 6-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:14.000 GCD[1148:97384] 7-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:14.000 GCD[1148:97384] 8-------<NSThread: 0x600000265400>{number = 3, name = (null)}
2017-09-13 00:12:14.003 GCD[1148:97384] 9-------<NSThread: 0x600000265400>{number = 3, name = (null)}
可以看到使用符合dispatch_block_t 形式的block都可以往隊(duì)列里去添加忙厌,而dispatch_block_t的類型如下圖所示
可以看到,它就是一個(gè)參數(shù)與返回值都為空的block拐邪。
3.四種情況
兩種隊(duì)列和兩種執(zhí)行方式組合起來有四種效果慰毅,分別是同步并行 同步串行 異步并行 和異步串行,
四種方式分別有不同的效果扎阶。在這寫條件里,執(zhí)行方式起主導(dǎo)作用婶芭,也就是同步和異步執(zhí)行起決定因素东臀。
1)同步并行
- (IBAction)syncconcurrence:(id)sender {
NSLog(@"開始");
//創(chuàng)建并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
//向隊(duì)列中添加任務(wù)一
dispatch_sync(queue, _block);
//向隊(duì)列中添加任務(wù)二
dispatch_sync(queue, _block);
//向隊(duì)列中添加任務(wù)三
dispatch_sync(queue, _block);
NSLog(@"等不等我");
}
運(yùn)行效果如下
2017-09-13 00:25:32.730 GCD[1181:107620] 開始
2017-09-13 00:25:32.731 GCD[1181:107620] 0-------<NSThread: 0x60000007b980>{number = 1, name = main}
2017-09-13 00:25:32.731 GCD[1181:107620] 1-------<NSThread: 0x60000007b980>{number = 1, name = main}
2017-09-13 00:25:32.731 GCD[1181:107620] 0-------<NSThread: 0x60000007b980>{number = 1, name = main}
2017-09-13 00:25:32.731 GCD[1181:107620] 1-------<NSThread: 0x60000007b980>{number = 1, name = main}
2017-09-13 00:25:32.732 GCD[1181:107620] 0-------<NSThread: 0x60000007b980>{number = 1, name = main}
2017-09-13 00:25:32.732 GCD[1181:107620] 1-------<NSThread: 0x60000007b980>{number = 1, name = main}
2017-09-13 00:25:32.733 GCD[1181:107620] 等不等我
可以看到結(jié)果是按添加到隊(duì)列里的順序,依次在主線程執(zhí)行犀农。
同步意味著阻塞惰赋,等隊(duì)列里的任務(wù)執(zhí)行完 才能繼續(xù)往下執(zhí)行NSLog(@"等不等我")這句代碼,并且不會開辟新線程,盡量在原有線程里執(zhí)行隊(duì)列里的任務(wù)赁濒,雖然隊(duì)列是并發(fā)隊(duì)列,隊(duì)列想要系統(tǒng)去開辟多個(gè)線程去執(zhí)行,但是前面的執(zhí)行方式已經(jīng)限定晦墙,系統(tǒng)不能開辟新線程择葡,只能在原有線程里面去執(zhí)行隊(duì)列里的任務(wù),結(jié)果跟單線程一樣击你,所以最終結(jié)果是依次執(zhí)行隊(duì)列里的任務(wù)玉组。
2)同步串行
//同步串行
- (IBAction)syncseria:(id)sender {
NSLog(@"開始");
//創(chuàng)建并行隊(duì)列 這里使用NULL缺省表示創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", NULL);
//向隊(duì)列中添加任務(wù)一
dispatch_sync(queue, _block);
//向隊(duì)列中添加任務(wù)二
dispatch_sync(queue, _block);
//向隊(duì)列中添加任務(wù)三
dispatch_sync(queue, _block);
NSLog(@"等不等我");
}
運(yùn)行結(jié)果如下
2017-09-13 00:29:42.657 GCD[1196:110692] 開始
2017-09-13 00:29:42.657 GCD[1196:110692] 0-------<NSThread: 0x60800007f180>{number = 1, name = main}
2017-09-13 00:29:42.657 GCD[1196:110692] 1-------<NSThread: 0x60800007f180>{number = 1, name = main}
2017-09-13 00:29:42.658 GCD[1196:110692] 0-------<NSThread: 0x60800007f180>{number = 1, name = main}
2017-09-13 00:29:42.659 GCD[1196:110692] 1-------<NSThread: 0x60800007f180>{number = 1, name = main}
2017-09-13 00:29:42.659 GCD[1196:110692] 0-------<NSThread: 0x60800007f180>{number = 1, name = main}
2017-09-13 00:29:42.660 GCD[1196:110692] 1-------<NSThread: 0x60800007f180>{number = 1, name = main}
2017-09-13 00:29:42.660 GCD[1196:110692] 等不等我
同步的意義同上,串行意味著按添加到隊(duì)列里的順序去執(zhí)行隊(duì)列里的任務(wù)丁侄,所以該組合效果與上面的效果是相同的惯雳。
3)異步并行
//異步并行
- (IBAction)asyncconcurrence:(id)sender {
NSLog(@"開始");
//創(chuàng)建并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
//向隊(duì)列中添加任務(wù)一
dispatch_async(queue, _block);
//向隊(duì)列中添加任務(wù)二
dispatch_async(queue, _block);
//向隊(duì)列中添加任務(wù)三
dispatch_async(queue, _block);
NSLog(@"等不等我");
}
2017-09-13 00:53:26.554 GCD[1251:127458] 開始
2017-09-13 00:53:26.554 GCD[1251:127458] 等不等我
2017-09-13 00:53:26.554 GCD[1251:128289] 0-------<NSThread: 0x60800026e6c0>{number = 4, name = (null)}
2017-09-13 00:53:26.554 GCD[1251:128296] 0-------<NSThread: 0x600000079c80>{number = 5, name = (null)}
2017-09-13 00:53:26.554 GCD[1251:128297] 0-------<NSThread: 0x60800026da00>{number = 6, name = (null)}
2017-09-13 00:53:26.557 GCD[1251:128289] 1-------<NSThread: 0x60800026e6c0>{number = 4, name = (null)}
2017-09-13 00:53:26.558 GCD[1251:128296] 1-------<NSThread: 0x600000079c80>{number = 5, name = (null)}
2017-09-13 00:53:26.559 GCD[1251:128297] 1-------<NSThread: 0x60800026da00>{number = 6, name = (null)}
異步并行執(zhí)行不會阻塞當(dāng)前代碼執(zhí)行,系統(tǒng)將任務(wù)都添加到隊(duì)列后馬上繼續(xù)執(zhí)行下面的代碼鸿摇,由系統(tǒng)去隊(duì)列里取出任務(wù)石景,然后開辟數(shù)個(gè)線程(線程數(shù)量不確定,由系統(tǒng)決定)去執(zhí)行隊(duì)列里的任務(wù)拙吉。
4)異步串行
//異步串行
- (IBAction)asyncseria:(id)sender {
NSLog(@"開始");
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", NULL);
//向隊(duì)列中添加任務(wù)一
dispatch_async(queue, _block);
//向隊(duì)列中添加任務(wù)二
dispatch_async(queue, _block);
//向隊(duì)列中添加任務(wù)三
dispatch_async(queue, _block);
NSLog(@"等不等我");
}
運(yùn)行效果如下
2017-09-13 00:53:44.740 GCD[1251:127458] 開始
2017-09-13 00:53:44.740 GCD[1251:127458] 等不等我
2017-09-13 00:53:44.741 GCD[1251:128663] 0-------<NSThread: 0x60800026cfc0>{number = 7, name = (null)}
2017-09-13 00:53:44.741 GCD[1251:128663] 1-------<NSThread: 0x60800026cfc0>{number = 7, name = (null)}
2017-09-13 00:53:44.742 GCD[1251:128663] 0-------<NSThread: 0x60800026cfc0>{number = 7, name = (null)}
2017-09-13 00:53:44.743 GCD[1251:128663] 1-------<NSThread: 0x60800026cfc0>{number = 7, name = (null)}
2017-09-13 00:53:44.743 GCD[1251:128663] 0-------<NSThread: 0x60800026cfc0>{number = 7, name = (null)}
2017-09-13 00:53:44.743 GCD[1251:128663] 1-------<NSThread: 0x60800026cfc0>{number = 7, name = (null)}
與異步并行的相同點(diǎn)是都不會阻塞當(dāng)前的代碼執(zhí)行潮孽,系統(tǒng)將任務(wù)都添加到隊(duì)列后馬上繼續(xù)執(zhí)行下面的代碼,由系統(tǒng)去隊(duì)里里取出任務(wù)庐镐,開辟新線程去執(zhí)行隊(duì)列里的任務(wù)恩商。區(qū)別是串行執(zhí)行時(shí)候,隊(duì)列里的任務(wù)只能按順序依次執(zhí)行必逆,所以系統(tǒng)僅僅開辟一個(gè)新線程就可以完成任務(wù)怠堪。
四種組合,在實(shí)際開發(fā)中應(yīng)當(dāng)如何選让肌粟矿?
同步方式使用較少,因?yàn)橥椒绞綍枞?dāng)前線程损拢,并且按順序執(zhí)行隊(duì)列任務(wù)陌粹,這樣的效果跟沒有使用隊(duì)列的情況下并沒有什么差別,同步執(zhí)行也不會開辟新線程福压, 使用意義不是很大掏秩。
異步使用場景較多,尤其是涉及到耗時(shí)操作荆姆,比如網(wǎng)絡(luò)請求蒙幻。在發(fā)出網(wǎng)絡(luò)請求時(shí)候,我們肯定不能讓主線程等待網(wǎng)絡(luò)請求結(jié)果返回在繼續(xù)執(zhí)行胆筒,因此邮破,這里要用異步dispatch_async請求。
串行隊(duì)列
//創(chuàng)建并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", NULL);
dispatch_async(queue, ^{
//獲取開始執(zhí)行任務(wù)的相對時(shí)間 注意這個(gè)單位是納秒
uint64_t timeStrat = mach_absolute_time();
NSLog(@"%llu",timeStrat);
//模擬網(wǎng)絡(luò)請求 2秒后完成
sleep(2);
NSLog(@"已完成任務(wù)1");
//回到主隊(duì)列 也就是主線程里進(jìn)行UI操作
dispatch_async(dispatch_get_main_queue(), ^{
self.view.backgroundColor = [UIColor redColor];
});
});
dispatch_async(queue, ^{
//模擬網(wǎng)絡(luò)請求 2秒后完成
sleep(2);
NSLog(@"已完成任務(wù)2");
//回到主隊(duì)列 也就是主線程里進(jìn)行UI操作
dispatch_async(dispatch_get_main_queue(), ^{
self.view.backgroundColor = [UIColor greenColor];
});
});
dispatch_async(queue, ^{
//模擬網(wǎng)絡(luò)請求 2秒后完成
sleep(2);
NSLog(@"已完成任務(wù)3");
//回到主隊(duì)列 也就是主線程里進(jìn)行UI操作
dispatch_async(dispatch_get_main_queue(), ^{
self.view.backgroundColor = [UIColor purpleColor];
});
//獲取結(jié)束任務(wù)的相對時(shí)間 注意這個(gè)單位是納秒
uint64_t timeEnd = mach_absolute_time();
NSLog(@"%llu",timeEnd);
});
執(zhí)行結(jié)果如下:
2017-09-13 15:58:25.534 GCD[3052:231602] 24762306847072
2017-09-13 15:58:27.536 GCD[3052:231602] 已完成任務(wù)1
2017-09-13 15:58:29.537 GCD[3052:231602] 已完成任務(wù)2
2017-09-13 15:58:31.541 GCD[3052:231602] 已完成任務(wù)3
2017-09-13 15:58:31.541 GCD[3052:231602] 24768313894166
可以看到串行隊(duì)列會按任務(wù)的添加順序依次執(zhí)行。每個(gè)任務(wù)會耗時(shí)2秒抒和,完成隊(duì)列所有任務(wù)要花費(fèi)的時(shí)間約為6秒矫渔。
并行隊(duì)列
僅需要把創(chuàng)建隊(duì)列時(shí)候的第二個(gè)參數(shù)設(shè)置為DISPATCH_QUEUE_CONCURRENT
即可
//創(chuàng)建并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//模擬網(wǎng)絡(luò)請求 2秒后完成
sleep(2);
NSLog(@"已完成任務(wù)1");
//回到主隊(duì)列 也就是主線程里進(jìn)行UI操作
dispatch_async(dispatch_get_main_queue(), ^{
self.view.backgroundColor = [UIColor redColor];
});
});
dispatch_async(queue, ^{
//模擬網(wǎng)絡(luò)請求 2秒后完成
sleep(2);
NSLog(@"已完成任務(wù)2");
//回到主隊(duì)列 也就是主線程里進(jìn)行UI操作
dispatch_async(dispatch_get_main_queue(), ^{
self.view.backgroundColor = [UIColor greenColor];
});
});
dispatch_async(queue, ^{
//模擬網(wǎng)絡(luò)請求 2秒后完成
sleep(2);
NSLog(@"已完成任務(wù)3");
//回到主隊(duì)列 也就是主線程里進(jìn)行UI操作
dispatch_async(dispatch_get_main_queue(), ^{
self.view.backgroundColor = [UIColor purpleColor];
});
});
執(zhí)行效果如下
2017-09-13 15:51:43.429 GCD[3022:227333] 已完成任務(wù)2
2017-09-13 15:51:43.429 GCD[3022:227326] 已完成任務(wù)1
2017-09-13 15:51:43.429 GCD[3022:227332] 已完成任務(wù)3
可以看到并行不一定會按任務(wù)添加順序執(zhí)行,完全由系統(tǒng)取出任務(wù)摧莽,分配到不同線程去執(zhí)行庙洼,所以先后順序也會不同。