簡(jiǎn)介
GCD全稱Drand Central Dispatch
- 純
c語(yǔ)言,提供了非常多強(qiáng)大的函數(shù)
什么是GCD?
將任務(wù)添加到隊(duì)列,并且指定執(zhí)行任務(wù)的函數(shù)
GCD的優(yōu)勢(shì):
-
GCD是蘋(píng)果公司為多核的并行運(yùn)算提供的解決方案。 -
GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核) -
GCD會(huì)自動(dòng)管理線程周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
程序員只需要告訴GCD想執(zhí)行什么任務(wù),并不需要編寫(xiě)任何線程管理代碼
基礎(chǔ)寫(xiě)法
//任務(wù)
dispatch_block_t block = ^{
NSLog(@"hello world");
};
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//異步函數(shù)
dispatch_async(queue, block);
這就是將任務(wù)添加到隊(duì)列,并通過(guò)異步函數(shù)執(zhí)行。
任務(wù)使用
block封裝,任務(wù)的block沒(méi)有參數(shù)也沒(méi)有返回值。-
異步函數(shù) dispatch_async
不用等待當(dāng)前語(yǔ)句執(zhí)行完畢,就可以執(zhí)行下一條語(yǔ)句- 會(huì)開(kāi)啟線程執(zhí)行
block任務(wù),異步是多線程的代名詞
- 會(huì)開(kāi)啟線程執(zhí)行
-
同步函數(shù) dispatch_sync
必須等到當(dāng)前語(yǔ)句執(zhí)行完畢,才可以執(zhí)行下一條語(yǔ)句- 不會(huì)開(kāi)啟線程,
在當(dāng)前線程中執(zhí)行block任務(wù)
- 不會(huì)開(kāi)啟線程,
同步和異步耗能問(wèn)題
同步函數(shù)
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//同步函數(shù)
dispatch_sync(queue, ^{
});
NSLog(@"%f",CFAbsoluteTimeGetCurrent() - time);
打印結(jié)果:
0.000008
異步函數(shù)
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//異步函數(shù)
dispatch_async(queue, ^{
// NSLog(@"任務(wù)1");
});
NSLog(@"%f",CFAbsoluteTimeGetCurrent() - time);
0.000027
空任務(wù)時(shí),同步函數(shù)耗時(shí)更少。
添加一個(gè)NSLog任務(wù)
- 同步函數(shù)
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//同步函數(shù)
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1");
});
NSLog(@"%f",CFAbsoluteTimeGetCurrent() - time);
打印結(jié)果
任務(wù)1
0.000099
- 異步函數(shù)
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//異步步函數(shù)
dispatch_async(queue, ^{
NSLog(@"任務(wù)1");
});
NSLog(@"%f",CFAbsoluteTimeGetCurrent() - time);
打印結(jié)果:
0.000025
任務(wù)1
一個(gè)簡(jiǎn)簡(jiǎn)單單的打印語(yǔ)句,同步函數(shù)耗費(fèi)時(shí)間遠(yuǎn)大于異步函數(shù)。
同步和異步函數(shù)都會(huì)耗費(fèi)時(shí)間,異步函數(shù)多用來(lái)解決并發(fā)和多線程問(wèn)題。
隊(duì)列
串行隊(duì)列:按照任務(wù)指派的順序來(lái)執(zhí)行任務(wù),前一個(gè)執(zhí)行完,下一個(gè)才能執(zhí)行
并行隊(duì)列:能夠同時(shí)執(zhí)行一個(gè)或多個(gè)任務(wù),執(zhí)行任務(wù)的順序不一定
切勿將串行隊(duì)列、并行對(duì)隊(duì)列和同步、異步混淆
函數(shù)與隊(duì)列搭配

同步函數(shù)與串行隊(duì)列
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//同步函數(shù)
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1");
sleep(2);
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2");
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)3");
});
打印結(jié)果:
任務(wù)1
任務(wù)2
任務(wù)3
同步函數(shù)與并行隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步函數(shù)
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1");
sleep(2);
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2");
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)3");
});
打印結(jié)果
任務(wù)1
任務(wù)2
任務(wù)3
異步函數(shù)與串行隊(duì)列
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//同步函數(shù)
dispatch_async(queue, ^{
NSLog(@"任務(wù)1");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)5");
});
異步函數(shù)與并行隊(duì)列
//并行隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步函數(shù)
dispatch_async(queue, ^{
NSLog(@"任務(wù)1");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)5");
sleep(2);
});
打印結(jié)果
任務(wù)2
任務(wù)1
任務(wù)3
任務(wù)4
任務(wù)5
面試題
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"任務(wù)1");
dispatch_async(queue, ^{
NSLog(@"任務(wù)2");
dispatch_async(queue, ^{
NSLog(@"任務(wù)3");
});
NSLog(@"任務(wù)4");
});
NSLog(@"任務(wù)5");
}
異步函數(shù)和并發(fā)隊(duì)列不會(huì)阻塞線程。
打印結(jié)果:
任務(wù)1
任務(wù)5
任務(wù)2
任務(wù)4
任務(wù)3
- 修改一個(gè)同步函數(shù)
dispatch_queue_t queue = dispatch_queue_create("com.ly", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"任務(wù)1");
dispatch_async(queue, ^{
NSLog(@"任務(wù)2");
dispatch_sync(queue, ^{
NSLog(@"任務(wù)3");
});
NSLog(@"任務(wù)4");
});
NSLog(@"任務(wù)5");
打印結(jié)果為:
任務(wù)1
任務(wù)5
任務(wù)2
任務(wù)3
任務(wù)4
- 將隊(duì)列改為串行隊(duì)列
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
NSLog(@"任務(wù)1");
dispatch_async(queue, ^{
NSLog(@"任務(wù)2");
dispatch_sync(queue, ^{
NSLog(@"任務(wù)3");
});
NSLog(@"任務(wù)4");
});
NSLog(@"任務(wù)5");
打印結(jié)果:
任務(wù)1
任務(wù)5
任務(wù)2
崩潰

面試題2:
dispatch_queue_t queue = dispatch_queue_create("com.ly", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"0");
dispatch_sync(queue, ^{
NSLog(@"7");
});
dispatch_sync(queue, ^{
NSLog(@"8");
});
dispatch_sync(queue, ^{
NSLog(@"9");
});
答案為:AC
3必須在0之前,0在789之前,123無(wú)序 789 無(wú)序
//A. 1230789
//B. 1237890
//C. 3120798
//D. 2137890
主隊(duì)列與全局隊(duì)列
主隊(duì)列:
- 專門用來(lái)在主線程上調(diào)度任務(wù)的串行隊(duì)列
- 不會(huì)開(kāi)啟線程
- 如果當(dāng)前主線程有任務(wù)正在執(zhí)行,那么無(wú)論主隊(duì)列中被添加了什么任務(wù),都不會(huì)被調(diào)度
-dispatch_get_main_queue()
全局隊(duì)列
- 為了方便程序員的使用,蘋(píng)果提供了全局隊(duì)列
dispatch_get_global_queue(0, 0) - 全局隊(duì)列是一個(gè)并發(fā)隊(duì)列
- 在使用多線程開(kāi)發(fā)時(shí),如果對(duì)隊(duì)列沒(méi)有特殊要求,在執(zhí)行異步任務(wù)的時(shí)候,可以直接使用全局隊(duì)列
死鎖現(xiàn)象
主線程因?yàn)槟阃胶瘮?shù)的原因先執(zhí)行任務(wù),主隊(duì)列等待著主線程中的任務(wù)執(zhí)行完畢,在執(zhí)行自己的任務(wù),主線程和主隊(duì)列相互等待會(huì)造成死鎖。
GCD用法
- 單例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"創(chuàng)建單例");
});
- 同步/異步函數(shù)
//同步函數(shù)
dispatch_sync(queue, ^{
NSLog(@"3-%@",[NSThread currentThread]);
});
//異步函數(shù)
dispatch_async(queue, ^{
NSLog(@"4-%@",[NSThread currentThread]);
});
- dispatch_source
- 其
CPU負(fù)荷小,盡量不占用系統(tǒng)資源 - 聯(lián)結(jié)的優(yōu)勢(shì)
在任一線程上調(diào)用它的一個(gè)函數(shù)dispatch_source_merge_data后,會(huì)執(zhí)行dispatch_source實(shí)現(xiàn)定義好的句柄(可以把句柄簡(jiǎn)單理解為一個(gè)block),這個(gè)過(guò)程叫做Custom event,用戶事件。是dispatch_source支持的一種事件。
句柄是一種指向指針的指針,它指向的就是一個(gè)類或者結(jié)構(gòu),它和系統(tǒng)有很密切的關(guān)系。HINSTANCE(實(shí)例句柄)、HBITMAP(位圖句柄)、HDC(設(shè)備表述句柄)、HICON(圖標(biāo)句柄)等。這其中還有一個(gè)通用句柄,就是HANDLE
dispatch_source_create 創(chuàng)建源
dispatch_source_set_event_handler 設(shè)置源事件回調(diào)
dispatch_source_merge_data 源事件設(shè)置數(shù)據(jù)
dispatch_source_get_data 獲取源事件數(shù)據(jù)
dispatch_resume 繼續(xù)
dispatch_suspend 掛起
簡(jiǎn)單例子
- (void)viewDidLoad {
[super viewDidLoad];
self.totalComplete = 0;
self.queue = dispatch_queue_create("ly.com", NULL);
// 不依賴 runloop
// 和下層內(nèi)核 kenel-workloop交互
// DISPATCH_SOURCE_TYPE_TIMER 準(zhǔn)確
self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(self.source, ^{
NSLog(@"%@",[NSThread currentThread]);
NSUInteger value = dispatch_source_get_data(self.source);
self.totalComplete += value;
NSLog(@"進(jìn)度: %.2f",self.totalComplete/100.0);
self.progressView.progress = self.totalComplete/100.0;
});
self.isRunning = YES;
dispatch_resume(self.source);
}
- (IBAction)didClickStartOrPauseAction:(id)sender {
if (self.isRunning) {
dispatch_suspend(self.source);
dispatch_suspend(self.queue);
NSLog(@"已經(jīng)暫停");
self.isRunning = NO;
[sender setTitle:@"暫停中.." forState:UIControlStateNormal];
}else{
dispatch_resume(self.source);
dispatch_resume(self.queue);
NSLog(@"已經(jīng)執(zhí)行了");
self.isRunning = YES;
[sender setTitle:@"執(zhí)行中.." forState:UIControlStateNormal];
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"開(kāi)始了");
for (int i= 0; i<100; i++) {
dispatch_async(self.queue, ^{
if (!self.isRunning) {
NSLog(@"已經(jīng)暫停");
return;
}
sleep(1);
dispatch_source_merge_data(self.source, 1);
});
}
}
- 柵欄函數(shù)
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
NSLog(@"2-%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"3");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
1、2執(zhí)行完才會(huì)執(zhí)行3、4
柵欄函數(shù),堵塞的是隊(duì)列
保護(hù)數(shù)據(jù)寫(xiě)入
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<5000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
@synchronized (self) {
[self.mArray addObject:image];
}
});
}
多線程同時(shí)向數(shù)組中寫(xiě)入數(shù)據(jù)時(shí),可能會(huì)出現(xiàn)問(wèn)題,上面使用了同步鎖。也可以使用柵欄函數(shù):
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<5000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_barrier_async(concurrentQueue, ^{
[self.mArray addObject:image];
});
});
}
-
柵欄函數(shù)不能使用全局隊(duì)列
將上面代碼對(duì)列改為全局隊(duì)列,運(yùn)行會(huì)崩潰
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<5000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_barrier_async(concurrentQueue, ^{
[self.mArray addObject:image];
});
});
}
崩潰效果圖:

原因:
柵欄函數(shù)主要是攔截隊(duì)列,不讓隊(duì)列后面的任務(wù)執(zhí)行。而系統(tǒng)的全局隊(duì)列,不只是添加了我們寫(xiě)的任務(wù),系統(tǒng)也會(huì)使用這個(gè)隊(duì)列,添加任務(wù);被攔截才導(dǎo)致崩潰
- 調(diào)度組 dispatch_group_t
__block int a, b;
dispatch_queue_t queue = dispatch_queue_create("com.ly", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
a = 3;
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
b = 4;
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"%d",a+b);
});
- 進(jìn)組和出組必須成對(duì)存在
- 只有當(dāng)
group中的任務(wù)都執(zhí)行完畢才會(huì)走dispatch_group_notify里的方法 -
dispatch_group_async相當(dāng)于dispatch_group_enter()和dispatch_group_leave()
- 延遲
- 線程通訊
- 信號(hào)量/鎖
信號(hào)量的簡(jiǎn)單使用:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t sem = dispatch_semaphore_create(2);
//任務(wù)1
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"執(zhí)行任務(wù)1");
sleep(1);
NSLog(@"任務(wù)1完成");
});
//任務(wù)2
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"執(zhí)行任務(wù)2");
sleep(1);
NSLog(@"任務(wù)2完成");
dispatch_semaphore_signal(sem);
});
//任務(wù)3
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"執(zhí)行任務(wù)3");
sleep(1);
NSLog(@"任務(wù)3完成");
dispatch_semaphore_signal(sem);
});
信號(hào)量創(chuàng)建的大小只有2,所以1、2任務(wù)會(huì)一起執(zhí)行;執(zhí)行完畢之后才會(huì)執(zhí)行3。
GCD不像NSOperation可以直接控制并發(fā)數(shù),設(shè)置maxConcurrentOperationCount;它需要借助信號(hào)量來(lái)實(shí)現(xiàn)這一需求。