1. GCD的基本使用
CGD是蘋果公司為多核的并行運(yùn)算提出的解決方案,會(huì)自動(dòng)利用更多的CPU內(nèi)核疙驾,自動(dòng)管理線程的生命周期(創(chuàng)建線程凶伙、調(diào)度任務(wù)、銷毀線程)它碎。So镊靴,我們只需告訴CGD勞資想執(zhí)行啥任務(wù)铣卡,不需要編寫任何線程管理的代碼。使用CGD來(lái)處理多線程問(wèn)題爽的不要不要的??
1.1 概念:
1.1.1 任務(wù)
任務(wù)就是執(zhí)行什么操作偏竟。任務(wù)分為同步任務(wù)和異步任務(wù)煮落,同步任務(wù)在當(dāng)前線程中執(zhí)行,異步任務(wù)在另一線程中執(zhí)行踊谋。
// 同步方式執(zhí)行任務(wù)
// queue:隊(duì)列
// block:任務(wù)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
// 異步方式執(zhí)行任務(wù)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
1.1.2 隊(duì)列
隊(duì)列就是存放任務(wù)的容器蝉仇。隊(duì)列分為串行隊(duì)列和并行隊(duì)列,并發(fā)隊(duì)列可以讓多個(gè)任務(wù)并發(fā)(即同時(shí))執(zhí)行(自動(dòng)開(kāi)啟多個(gè)線程同時(shí)執(zhí)行任務(wù))殖蚕,so轿衔,并發(fā)任務(wù)只有在異步下才有效哦??;串行隊(duì)列中任務(wù)一個(gè)一個(gè)排隊(duì)執(zhí)行睦疫,跟我們排隊(duì)打飯類似哦害驹!
CGD的使用就下面兩步:
- 定制任務(wù)(要干什么)
- 將任務(wù)添加到隊(duì)列中。CGD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出放到對(duì)應(yīng)的線程中執(zhí)行蛤育,任務(wù)的取出遵循隊(duì)列的FIFO原則宛官。
下面給大家show一把這4種組合??:
1.2. 串行隊(duì)列 + 同步任務(wù)
/**
1. 創(chuàng)建一個(gè)串行隊(duì)列
@param "lile" 隊(duì)列標(biāo)簽
@param DISPATCH_QUEUE_SERIAL 隊(duì)列的屬性, serial:連續(xù)的瓦糕,看來(lái)學(xué)好英文對(duì)編程很重要哦底洗!
@return 串行隊(duì)列
*/
dispatch_queue_t queue = dispatch_queue_create("lile", DISPATCH_QUEUE_SERIAL);
// 2. 同步執(zhí)行任務(wù)
// 一般只要使用”同步“執(zhí)行,串行隊(duì)列對(duì)添加的同步任務(wù)咕娄,會(huì)立馬執(zhí)行
dispatch_sync(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
不開(kāi)新線程亥揖,在當(dāng)前線程(即主線程)順序執(zhí)行
1.3 串行隊(duì)列 + 異步執(zhí)行
// 1. 串行隊(duì)列
// 下面兩種寫法是一樣的
dispatch_queue_t queue1 = dispatch_queue_create("lile", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("lile", NULL);
// 2. 異步執(zhí)行
for (int i = 0; i < 10; i++) {
dispatch_async(queue1, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
只會(huì)開(kāi)一個(gè)線程(非主線程),而且所有任務(wù)都在這個(gè)新的線程里面執(zhí)行
1.4 并行隊(duì)列 + 同步執(zhí)行
// 1. 并行隊(duì)列圣勒,concurrent:并發(fā)的费变,同時(shí)發(fā)生的
dispatch_queue_t queue = dispatch_queue_create("lile", DISPATCH_QUEUE_CONCURRENT);
// 2. 同步執(zhí)行任務(wù)
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
不開(kāi)新線程,順序一個(gè)一個(gè)執(zhí)行
1.5 并行隊(duì)列 + 異步執(zhí)行
// 1. 并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("lile", DISPATCH_QUEUE_CONCURRENT);
// 2. 異步執(zhí)行任務(wù)
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
會(huì)開(kāi)很多個(gè)線程圣贸,同時(shí)執(zhí)行
1.6 主隊(duì)列 + 同步執(zhí)行
// 1. 主隊(duì)列:專門負(fù)責(zé)在主線程上調(diào)度任務(wù)挚歧,不會(huì)在子線程調(diào)度任務(wù),在主隊(duì)列不允許開(kāi)新線程
dispatch_queue_t queue = dispatch_get_main_queue();
// 2. 同步執(zhí)行任務(wù)
for (int i = 0; i < 10; i++) {
// 同步:把任務(wù)放到主隊(duì)列里旁趟,但需是馬上執(zhí)行
dispatch_sync(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
[NSThread sleepForTimeInterval:2.0];
}
??會(huì)發(fā)生死鎖哦昼激!
1.7 主隊(duì)列 + 異步執(zhí)行
// 1. 獲得主隊(duì)列-> 程序啟動(dòng)庇绽,--> 至少有一個(gè)主線程-> 一開(kāi)始就會(huì)創(chuàng)建主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"1----");
// 2. 異步執(zhí)行任務(wù)
for (int i = 0; i < 10; i++) {
NSLog(@"調(diào)度前---");
// 異步:把任務(wù)放到主隊(duì)列里锡搜,但是不需要馬上執(zhí)行
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
[NSThread sleepForTimeInterval:2.0];
}
不開(kāi)線程, 只能在主線程上面瞧掺,順序執(zhí)行!
1.8 全局隊(duì)列
// 獲得全局隊(duì)列
/**
參數(shù):第一個(gè)參數(shù)耕餐,一般 寫 0(可以適配 iOS 7 & 8)
iOS 7
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í)
iOS 8
QOS_CLASS_DEFAULT 0
第二個(gè)參數(shù):保留參數(shù) 0
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 添加異步任務(wù)
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
開(kāi)子線程,在子線程中無(wú)序執(zhí)行
Tip: 全局隊(duì)列和并行隊(duì)列的區(qū)別:
全局隊(duì)列沒(méi)有名稱辟狈,并發(fā)隊(duì)列有名字
全局隊(duì)列是供所有的應(yīng)用程序共享
在在MRC開(kāi)發(fā)肠缔,并發(fā)隊(duì)列夏跷,創(chuàng)建完了,需要釋放明未。 全局隊(duì)列不需要我們管理
2. 一些特殊的使用場(chǎng)景
2.1 同步任務(wù)的使用場(chǎng)景
// 并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("lile", DISPATCH_QUEUE_CONCURRENT);
/**
例子:有一個(gè)小說(shuō)網(wǎng)站
- 必須登錄槽华,才能下載小說(shuō)
有三個(gè)任務(wù):
1. 用戶登錄
2. 下載小說(shuō)A
3. 下載小說(shuō)B
*/
// 添加任務(wù)
// 同步任務(wù),需要馬上執(zhí)行趟妥。 不開(kāi)新線程
dispatch_sync(queue, ^{
NSLog(@"用戶登錄 %@", [NSThread currentThread]);
});
// 異步任務(wù)猫态,子線程中執(zhí)行耗時(shí)操作
dispatch_async(queue, ^{
NSLog(@"下載小說(shuō)A %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"下載小說(shuō)B %@", [NSThread currentThread]);
});
關(guān)于隊(duì)列的選擇:
串行隊(duì)列異步執(zhí)行:開(kāi)一條線程,順序執(zhí)行披摄。慢亲雪,但資源占用少,省電疚膊。一般手機(jī)網(wǎng)絡(luò)時(shí)使用义辕,對(duì)性能要求不太高
并發(fā)隊(duì)列異步執(zhí)行:開(kāi)啟多條線程,同時(shí)執(zhí)行寓盗」嘧快,但資源消耗大贞让,費(fèi)電周崭。一般在WI-FI環(huán)境或需要很快響應(yīng)的場(chǎng)景下使用。
同步任務(wù):一般只會(huì)在并發(fā)隊(duì)列喳张, 需要阻塞后續(xù)任務(wù)续镇。必須等待同步任務(wù)執(zhí)行完畢,再去執(zhí)行其他任務(wù)销部。存在"依賴"關(guān)系摸航。
2.2 延時(shí)操作
/**
NSEC_PER_SEC: 很大的數(shù)字
*/
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
/**
多長(zhǎng)時(shí)間后執(zhí)行任務(wù)
@param when 表示從現(xiàn)在開(kāi)始,經(jīng)過(guò)多少納秒以后
@param queue 在那個(gè)隊(duì)列中執(zhí)行任務(wù)
@param block 待執(zhí)行的任務(wù)
*/
dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
NSLog(@" %@", [NSThread currentThread]);
});
2.3 調(diào)度組(分組)
/**
應(yīng)用場(chǎng)景:
開(kāi)發(fā)的時(shí)候舅桩,有的時(shí)候出現(xiàn)多個(gè)網(wǎng)絡(luò)請(qǐng)求都完成以后(每一個(gè)網(wǎng)絡(luò)請(qǐng)求的事件長(zhǎng)短不一定)酱虎,再統(tǒng)一通知用戶
比如: 下載小說(shuō):三國(guó)演義, 紅樓夢(mèng)擂涛, 金X梅
*/
// 實(shí)例化一個(gè)調(diào)度組
dispatch_group_t group = dispatch_group_create();
// 隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 任務(wù)添加到隊(duì)列queue
dispatch_group_async(group, queue, ^{
NSLog(@"下載小說(shuō)A---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下載小說(shuō)B---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下載小說(shuō)X---%@", [NSThread currentThread]);
});
// 獲得所有調(diào)度組里面的異步任務(wù)完成的通知
dispatch_group_notify(group, queue, ^{
NSLog(@"下載完成读串,請(qǐng)觀看%@", [NSThread currentThread]); // 異步的
});
//注意點(diǎn): 在調(diào)度組完成通知里,可以跨隊(duì)列通信
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 更新UI撒妈,在主線程
NSLog(@"下載完成恢暖,請(qǐng)觀看%@", [NSThread currentThread]); // 異步的
});
2.4 一次性執(zhí)行
多用于創(chuàng)建單例
static dispatch_once_t onceToken;
NSLog(@"%ld", onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"%----ld", onceToken);
NSLog(@"真的執(zhí)行一次么?");
});
NSLog(@"完成");
3. GCD中線程間的通信
// 全局隊(duì)列 + 異步任務(wù)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@", [NSThread currentThread]);
// 耗時(shí)操作: 放在全局隊(duì)列狰右,異步執(zhí)行
// 1. url, 確定一個(gè)網(wǎng)絡(luò)上的資源路徑
NSURL *url = [NSURL URLWithString:@"http://fe.topit.me/e/d1/12/1170068721aa112d1el.jpg"];
// 2. 通過(guò)url可以下載對(duì)應(yīng)的網(wǎng)絡(luò)資源, 網(wǎng)絡(luò)資源傳輸?shù)亩际嵌M(jìn)制
NSData *data = [NSData dataWithContentsOfURL:url];
// 3. 二進(jìn)制轉(zhuǎn)成圖片
UIImage *image = [UIImage imageWithData:data];
// 4. 更新UI杰捂,在主線程-》 直接把任務(wù)添加到主隊(duì)列,就會(huì)在主隊(duì)列執(zhí)行
dispatch_async(dispatch_get_main_queue(), ^{
self.iconView.image = image;
NSLog(@"-----%@", [NSThread currentThread]);
});
});