簡(jiǎn)介
GCD
全稱Drand Central Dispatch
- 純
c
語(yǔ)言,提供了非常多強(qiáng)大的函數(shù)
什么是GCD
?
將任務(wù)添加到隊(duì)列,并且指定執(zhí)行任務(wù)的函數(shù)
GCD
的優(yōu)勢(shì):
-
GCD
是蘋果公司為多核的并行運(yùn)算提供的解決方案。 -
GCD
會(huì)自動(dòng)利用更多的
CPU內(nèi)核
(比如雙核员魏、四核) -
GCD
會(huì)自動(dòng)管理線程周期
(創(chuàng)建線程、調(diào)度任務(wù)叠聋、銷毀線程)
程序員只需要告訴GCD
想執(zhí)行什么任務(wù)撕阎,并不需要編寫任何線程管理代碼
基礎(chǔ)寫法
//任務(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ì)開啟線程執(zhí)行
block
任務(wù)魄眉,異步是多線程的代名詞
- 會(huì)開啟線程執(zhí)行
-
同步函數(shù) dispatch_sync
必須等到當(dāng)前語(yǔ)句執(zhí)行完畢砰盐,才可以執(zhí)行下一條語(yǔ)句- 不會(huì)開啟線程,
在當(dāng)前線程中執(zhí)行block任務(wù)
- 不會(huì)開啟線程,
同步和異步耗能問(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ì)開啟線程
- 如果當(dāng)前主線程有任務(wù)正在執(zhí)行,那么無(wú)論主隊(duì)列中被添加了什么任務(wù)缰猴,都不會(huì)被調(diào)度
-dispatch_get_main_queue()
全局隊(duì)列
- 為了方便程序員的使用产艾,蘋果提供了全局隊(duì)列
dispatch_get_global_queue(0, 0)
- 全局隊(duì)列是一個(gè)并發(fā)隊(duì)列
- 在使用多線程開發(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(@"開始了");
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ù)寫入
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ù)組中寫入數(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ì)列,不只是添加了我們寫的任務(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)這一需求宙枷。