一.什么是GCD猎唁?
1.GCD (Grand Central Dispatch) 是Apple公司開發(fā)的一種技術(shù)椎组,它旨在優(yōu)化多核環(huán)境中的并發(fā)操作并取代傳統(tǒng)多線程的編程模式泣洞。
2.GCD是基于C語言的線程管理方案宴抚,使用者無需過多參與線程的管理,只需要將想要執(zhí)行的代碼奠蹬,添加到想要添加的調(diào)度隊列即可。
3.GCD主要用在后臺執(zhí)行較慢任務(wù)嗡午;延遲執(zhí)行任務(wù)囤躁;以及在后臺任務(wù)中,切換回主線程荔睹,更新UI狸演。
二.GCD的優(yōu)勢
GCD是蘋果公司為多核的并行運算提出的解決方案。
GCD會自動利用更多的CPU內(nèi)核(比如雙核僻他、四核)宵距。
GCD會自動管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)吨拗、銷毀線程)满哪。
程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼劝篷。
三.任務(wù)和隊列
3.1 隊列
隊列:用于存放要執(zhí)行的任務(wù)哨鸭。隊列是一種特殊的線性表,采用FIFO(先進(jìn)先出)的原則娇妓,即新任務(wù)總是被插入到隊列的末尾像鸡,而讀取任務(wù)的時候總是從隊列的頭部開始讀取。每讀取一個任務(wù)哈恰,則從隊列中釋放一個任務(wù)只估。在 GCD中有兩種隊列:
- 串行隊列(Serial Dispatch Queue)
一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù)
- 并發(fā)隊列(Concurrent Dispatch Queue)
可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程執(zhí)行任務(wù))并發(fā)功能只有在異步(dispatch_async) 函數(shù)下才有效
DISPATCH_QUEUE_SERIAL: 表示串行蕊蝗,也可以使用NULL表示仅乓,因為宏定義定義的為NULL。
DISPATCH_QUEUE_CONCURRENT:表示并發(fā)
3.2 任務(wù)
任務(wù):在線程中執(zhí)行的操作蓬戚,GCD中就是指Block中的代碼夸楣。任務(wù)有兩種方式:同步執(zhí)行(sync)和異步執(zhí)行(async)
。區(qū)別在于是否會創(chuàng)建新的線程。同步和異步的主要區(qū)別在于會不會阻塞當(dāng)前線程豫喧,直到Block中的任務(wù)執(zhí)行完畢石洗。
- 同步執(zhí)行:當(dāng)前任務(wù)不完成,不會執(zhí)行下個任務(wù)紧显。
- 異步執(zhí)行:當(dāng)前任務(wù)不完成讲衫,不會等待,同樣可以執(zhí)行下個任務(wù)孵班。
四.GCD的串行隊列涉兽,并發(fā)隊列和全局隊列
各種隊列的執(zhí)行效果:
4.1并發(fā)隊列
1.并發(fā)隊列,同步任務(wù)(不會開啟新的線程篙程,并發(fā)隊列失去了并發(fā)的功能枷畏。)
//1.創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_CONCURRENT);
//2.添加任務(wù)到隊列中執(zhí)行
for (int i=0; i<10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
總結(jié):不會開啟線程,并且會順序執(zhí)行虱饿。
2.并發(fā)隊列拥诡,異步任務(wù)(執(zhí)行較慢的任務(wù),例如大量計算氮发,網(wǎng)絡(luò)請求等渴肉。)
//1.創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_CONCURRENT);
//2.添加任務(wù)到隊列中執(zhí)行
for (int i=0; i<10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
總結(jié):會開啟線程,不會順序執(zhí)行爽冕。
4.2串行隊列
1.串行隊列仇祭,同步任務(wù)(在新線程中執(zhí)行任務(wù),并且等待線程執(zhí)行完畢再向后執(zhí)行扇售,幾乎不用)
//1.創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_SERIAL);
//2.添加任務(wù)到隊列中執(zhí)行
for (int i=0; i<10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
2.串行隊列前塔,異步任務(wù)
dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_SERIAL);
for (int i=0; i<10; i++) {
NSLog(@"%d-----",i); //主線程,會打印完9之后承冰,才會執(zhí)行come here
dispatch_async(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i); //子線程
});
}
NSLog(@"come here"); //在主線程华弓,和隊列沒有任何關(guān)系
2021-03-23 23:35:59.401760+0800 多線程Demo[32679:1746588] 0-----
2021-03-23 23:35:59.401965+0800 多線程Demo[32679:1746588] 1-----
2021-03-23 23:35:59.402048+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 0
2021-03-23 23:35:59.402079+0800 多線程Demo[32679:1746588] 2-----
2021-03-23 23:35:59.402199+0800 多線程Demo[32679:1746588] 3-----
2021-03-23 23:35:59.402266+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 1
2021-03-23 23:35:59.402307+0800 多線程Demo[32679:1746588] 4-----
2021-03-23 23:35:59.402397+0800 多線程Demo[32679:1746588] 5-----
2021-03-23 23:35:59.402404+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 2
2021-03-23 23:35:59.402680+0800 多線程Demo[32679:1746588] 6-----
2021-03-23 23:35:59.402908+0800 多線程Demo[32679:1746588] 7-----
2021-03-23 23:35:59.403205+0800 多線程Demo[32679:1746588] 8-----
2021-03-23 23:35:59.403466+0800 多線程Demo[32679:1746588] 9-----
2021-03-23 23:35:59.403737+0800 多線程Demo[32679:1746588] come here
2021-03-23 23:35:59.404267+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 3
2021-03-23 23:35:59.404670+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 4
2021-03-23 23:35:59.405344+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 5
2021-03-23 23:35:59.405665+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 6
2021-03-23 23:35:59.406199+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 7
2021-03-23 23:35:59.407880+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 8
2021-03-23 23:35:59.408041+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 9
- 首先肯定是打印完9之后,才會打印come here困乒,因為都是在主線程寂屏,然后打印數(shù)字和打印線程是交叉執(zhí)行的。
總結(jié):會開啟新的線程娜搂,任務(wù)會順序完成迁霎。
4.3主隊列
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
});
- 總結(jié):不會開啟新線程,串行執(zhí)行任務(wù)百宇。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@“在同步主線程中執(zhí)行考廉,慎用,否則會死鎖”);
});
- 總結(jié):使用sync函數(shù)往當(dāng)前串行隊列中添加任務(wù)携御,會卡住當(dāng)前的串行隊列(產(chǎn)生死鎖)
五.GCD的其他用法以及注意事項
5.1異步主線程(用于在后臺線程的任務(wù)將要完成時昌粤,切換到主線程更新UI)(不會開新線程)
//主隊列是專門負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊列 --> 不會開線程
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"在異步主線程中執(zhí)行");
});
// 執(zhí)?耗時的異步操作...
dispatch_async( dispatch_get_global_queue(0, 0), ^{ //請求數(shù)據(jù)
dispatch_async(dispatch_get_main_queue(), ^{ // 回到主線程,執(zhí)?UI刷新操作
//對圖片或別的操作進(jìn)行賦值等既绕,回到主線程
});
});
5.2全局隊列(相當(dāng)于并發(fā)隊列)
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
});
5.3延時執(zhí)行線程
//延遲
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"我在五秒后打印");
});
5.4單例模式(只執(zhí)行一次)
static GGT_Singleton *singleton = nil; //在.m中保留一個全局的static的實例
+ (GGT_Singleton *)sharedSingleton {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[GGT_Singleton alloc]init];
});
return singleton;
}
5.5隊列組
如果有三個異步操作(例:網(wǎng)絡(luò)請求ABC),想在三個請求全部執(zhí)行完返回結(jié)果后涮坐,再做其他操作凄贩。
1、這三個網(wǎng)絡(luò)請求袱讹,執(zhí)行無序疲扎,并發(fā)執(zhí)行。
2捷雕、不論返回失敗還是成功都算返回結(jié)果椒丧。
如何處理?
如果滿足上面的需求非区,需要使用GCD里面的dispatch_group_enter和dispatch_group_leave來實現(xiàn)瓜挽。
dispatch_group_enter:通知 group,下個任務(wù)要放入 group 中執(zhí)行了。
dispatch_group_leave:通知 group,任務(wù)成功完成,要移除,與enter()成對出現(xiàn)征绸。
dispatch_group_notify:只要任務(wù)組完成才會調(diào)用,不完成不會調(diào)用俄占。
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
NSLog(@"1 %@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^{
NSLog(@"2 %@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后管怠,回到主線程...
NSLog(@"結(jié)束");
});
2022-02-11 18:18:51.877309+0800 TestDemo[14721:238852] 1 <NSThread: 0x6000026d6d00>{number = 3, name = (null)}
2022-02-11 18:18:53.071275+0800 TestDemo[14721:238852] 2 <NSThread: 0x6000026d6d00>{number = 3, name = (null)}
2022-02-11 18:18:53.071532+0800 TestDemo[14721:238763] 結(jié)束
5.6快速迭代(使用dispatch_apply函數(shù)能進(jìn)行快速迭代遍歷)
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){ //%zu用來輸出size_t 類型
// 執(zhí)行10次代碼,index順序不確定
NSLog(@"%zu",index);
});
5.7 iOS中的多讀單寫安全方案
//初始化隊列
dispatch_queue_t queue = dispatch_queue_create("rw_queue",DISPATCH_QUEUE_CONCURRENT);
//讀
dispatch_async(queue, ^{
});
//寫
dispatch_barrier_async(queue, ^{
});
六.小結(jié)
- 同步任務(wù)死鎖:當(dāng)前是在主線程,讓主隊列執(zhí)行同步任務(wù)!
//主隊列是專門負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊列 --> 不會開線程
//1.隊列 --> 已啟動主線程,就可以獲取主隊列
dispatch_queue_t q = dispatch_get_main_queue();
//2.同步任務(wù) ==> 死鎖
dispatch_sync(q, ^{
NSLog(@"能來嗎? ");
});
NSLog(@"come here");
解決辦法:
void (^task)() = ^{
NSLog(@"這里%@",[NSThread currentThread]);
//1.隊列 --> 已啟動主線程,就可以獲取主隊列
dispatch_queue_t q = dispatch_get_main_queue();
//2.同步任務(wù)
dispatch_sync(q, ^{
NSLog(@"能來嗎? %@",[NSThread currentThread]);
});
NSLog(@"come here");
};
dispatch_async(dispatch_get_global_queue(0, 0), task);
競爭&同步:兩個線程搶奪同一個資源缸榄,就會競爭渤弛,為了防止競爭,一個線程擁有資源的時候甚带,會對資源加鎖她肯,另一個線程就要等待解鎖以后再擁有這個資源,這叫同步鹰贵。
死鎖:兩個線程互相等待對方釋放資源晴氨。
主線程&后臺線程:主線程也叫前臺線程,程序啟動的默認(rèn)線程碉输,操作UI的線程籽前。后臺線程,即非主線程敷钾,用于不影響主線程的完成一些任務(wù)枝哄。
同步&異步:同步執(zhí)行線程,等待新線程執(zhí)行完以后阻荒,再繼續(xù)執(zhí)行當(dāng)前線程挠锥,很少用到。異步執(zhí)行線程侨赡,在執(zhí)行新線程的同時蓖租,繼續(xù)執(zhí)行當(dāng)前線程纱控,常用。
iOS 多線程入門01--概念知識
iOS 多線程入門02--NSThread
iOS 多線程入門04--NSOperation