今天和大家一起來討論一下GCD的基本使用,有疏忽的地方,還望各位不吝賜教。
一、多線程簡(jiǎn)介
1、多線程相關(guān)的概念
進(jìn)程:
1、進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序;
2、每個(gè)進(jìn)程是獨(dú)立的,每個(gè)進(jìn)程運(yùn)行在專用且受保護(hù)的內(nèi)存空間中。
線程:
1、一個(gè)進(jìn)程執(zhí)行任務(wù),必須依賴線程(一個(gè)進(jìn)程至少有一條線程);
2、進(jìn)程的所有任務(wù)都在線程中執(zhí)行。
線程的串行:
1、一個(gè)線程中任務(wù)的執(zhí)行是串行的
2、如果要在一個(gè)線程中執(zhí)行多個(gè)任務(wù),只能一個(gè)一個(gè)按順序執(zhí)行,即同一時(shí)間一個(gè)線程只能執(zhí)行一個(gè)任務(wù);
線程和進(jìn)程的比較:
1、線程是CPU調(diào)用(執(zhí)行任務(wù))的最小單位。
2、進(jìn)程是CPU分配資源的最小單位。
3、一個(gè)程序可以對(duì)應(yīng)多個(gè)進(jìn)程,一個(gè)進(jìn)程中可以有多個(gè)線程,但至少有一個(gè)。
4、同一個(gè)進(jìn)程中的線程共享進(jìn)程的資源。
多線程:
一個(gè)進(jìn)程中可以開啟多條線程的,每一條線程可以并行(同時(shí))執(zhí)行不同的任務(wù)。其實(shí)在同一時(shí)間,CPU只能處理一條線程,多線程的并發(fā)(同時(shí))執(zhí)行,其實(shí)是一種假象,只不過是快速的在多條線程之間進(jìn)行調(diào)度,也就是切換罷了。但是一般不建議開很多條線程,因?yàn)殚_線程越多,相應(yīng)的開銷就越大,對(duì)于CPU來說負(fù)擔(dān)就越重。
2、多線程的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1、能適當(dāng)?shù)奶岣叱绦虻膱?zhí)行效率;
2、能適當(dāng)?shù)奶岣哔Y源的利用率(包括CPU和內(nèi)存)。
缺點(diǎn):
1、創(chuàng)建線程是有開銷的,iOS 9下主要成本包括:內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)、棧空間(子線程512KB,主線程1MB,也可以使用-setStackSize:進(jìn)行設(shè)置,但是必須是4KB的倍數(shù),最小16KB),創(chuàng)建線程大約需要90毫秒的時(shí)間進(jìn)行創(chuàng)建。
2、如果開啟大量的線程,會(huì)降低程序的性能。
3、線程越多,CPU在線程上的開銷就越大。
4、程序設(shè)計(jì)更加復(fù)雜,包括在線程之間的通信和多線程之間的數(shù)據(jù)共享。
3、多線程在iOS中的應(yīng)用
主線程
一個(gè)iOS程序運(yùn)行后,默認(rèn)會(huì)開啟一條線程,稱為主線程或者UI線程。
主線程的主要作用
1、顯示\刷新UI界面;
2、處理UI事件(比如點(diǎn)擊事件,滾動(dòng)事件和拖拽事件等);
3、注意不要將耗時(shí)操作放到主線程匯總;
4、耗時(shí)操作會(huì)卡住主線程,嚴(yán)重影響UI的流暢度,造成不好的用戶體驗(yàn);
5、在開發(fā)過程中,注意將耗時(shí)操作放到子線程中進(jìn)行執(zhí)行。
二、多線程安全
當(dāng)多個(gè)線程同時(shí)訪問一塊資源的時(shí)候,會(huì)發(fā)生數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全的問題。
解決方案:互斥鎖
當(dāng)線程A訪問資源的時(shí)候加鎖,保證其他線程無法在線程A訪問期間進(jìn)行對(duì)本資源的操作,在線程A操作完成后,會(huì)打開添加的互斥鎖,之后其他線程可以按照同樣的方式進(jìn)行訪問資源。
格式:
@synchronized(鎖對(duì)象) {需要鎖定的代碼}
使用方式:
@synchronized(self) {}
注意點(diǎn):
1、鎖必須是全局唯一的,所以一般使用控制器作為標(biāo)識(shí)。
2、注意加鎖的前提條件,在多線程訪問同一塊資源的時(shí)候再添加。
3、加鎖是需要耗費(fèi)性能的。
4、加鎖的結(jié)果:線程同步(多條線程在同一條線上執(zhí)行,按照順序進(jìn)行執(zhí)行)
關(guān)于原子屬性和非原子屬性的解釋:
*atomic:
原子屬性,會(huì)為setter方法加鎖,默認(rèn)為atomic。
因?yàn)闀?huì)為setter方法加鎖,所以在使用的時(shí)候是線程安全的,但是會(huì)消耗大量的資源。
*nonatomic:
非原子屬性,不會(huì)為setter方法加鎖。
雖然nonatomic是不安全的,但是出現(xiàn)多個(gè)線程同時(shí)訪問一塊資源的情況很少,為了保證效率,所以一般使用nonatomic。
建議:
盡量把這些爭(zhēng)奪資源的任務(wù)交給服務(wù)器端處理,減小移動(dòng)端的壓力。
三、iOS多線程實(shí)現(xiàn)方案
| 技術(shù)方案 | 簡(jiǎn)介 | 語言 | 線程生命周期 | 使用頻率 |
|---|---|---|---|---|
| pthread | 一套通用的多線程API 可移植,適用于多個(gè)平臺(tái) 使用難度大 |
C | 程序員管理 | 幾乎不用 |
| NSThread | 使用更加面向?qū)ο?br>簡(jiǎn)單易用,可以直接操作線程對(duì)象 | OC | 程序員管理 | 偶爾使用 |
| GCD | 為了替代NSThread等的線程技術(shù) 充分利用設(shè)備的多核 |
C | 自動(dòng)管理 | 經(jīng)常使用 |
| NSOperation | 基于GCD 相比GCD多了一些更簡(jiǎn)單實(shí)用的功能 更加面向?qū)ο?/td> | OC | 自動(dòng)管理 | 經(jīng)常使用 |
四、GCD多線程實(shí)現(xiàn)方案
1、GCD簡(jiǎn)介
全稱是Grand Central Dispatch,是一個(gè)純C語言的庫,是蘋果公司為多核的并行運(yùn)算提出的解決方案。
2、GCD優(yōu)勢(shì)
1、GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核和四核)
2、GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程,調(diào)度線程和銷毀線程)
3、程序員不需要手動(dòng)編寫管理線程的任何代碼,只需要告訴GCD想要執(zhí)行什么任務(wù)
3、任務(wù)和隊(duì)列
任務(wù):執(zhí)行的操作統(tǒng)稱為任務(wù)
隊(duì)列:用來存放任務(wù),并且調(diào)度任務(wù)在那些線程中執(zhí)行
4、使用步驟
1、定制任務(wù)
確定想做的任務(wù)
2、將任務(wù)添加到隊(duì)列中
GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出來,放到對(duì)應(yīng)的線程中執(zhí)行
任務(wù)的取出遵循隊(duì)列的FIFO的原則:先進(jìn)先出,后進(jìn)后出
5、執(zhí)行任務(wù)的方式
GCD中有兩個(gè)用來執(zhí)行任務(wù)的常用函數(shù)
- 同步函數(shù)
同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力 - 異步函數(shù)
異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
6、隊(duì)列的類型
GCD中隊(duì)列分為兩大類型
- 并發(fā)隊(duì)列(Concurrent Dispatch Queue)
可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù)) - 串行隊(duì)列 (Serial Dispatch Queue)
可以讓任務(wù)一個(gè)接著一個(gè)的執(zhí)行(一個(gè)執(zhí)行完之后,再執(zhí)行下一個(gè)任務(wù))
7、術(shù)語的相關(guān)解釋
- 同步和異步主要影響的是能不能開啟新的線程 這里指的是函數(shù)
同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力 - 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式 這里指的是隊(duì)列
并發(fā):可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
串行:可以讓任務(wù)一個(gè)接著一個(gè)的執(zhí)行(一個(gè)執(zhí)行完之后,再執(zhí)行下一個(gè)任務(wù))
五、pthread簡(jiǎn)單使用
因?yàn)閜thread用的不多,所以簡(jiǎn)單提一下,關(guān)于其創(chuàng)建線程的使用如下:
// 1、創(chuàng)建線程對(duì)象
pthread_t thread;
// 2、創(chuàng)建線程
/*
* 第一個(gè)參數(shù):線程對(duì)象,傳遞地址
* 第二個(gè)參數(shù):線程的屬性 NULL
* 第三個(gè)參數(shù):指向函數(shù)的指針
* 第四個(gè)參數(shù):函數(shù)需要接受的參數(shù)
*/
pthread_create(&thread, NULL, task, NULL);
// 3、task實(shí)現(xiàn)
void *task(void *param){
NSLog(@"%@",[NSThread currentThread]);
return NULL;
}
六、GCD簡(jiǎn)單使用
創(chuàng)建并發(fā)隊(duì)列的方式
1、create創(chuàng)建并發(fā)隊(duì)列(主動(dòng)創(chuàng)建)
/*
* 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
* 第二個(gè)參數(shù):隊(duì)列的類型
* DISPATCH_QUEUE_CONCURRENT 并發(fā)
* DISPATCH_QUEUE_SERIAL 串行
*/
dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_CONCURRENT);
2、獲取全局并發(fā)隊(duì)列(系統(tǒng)中本身存在,拿過來用就行了)
/* 和DISPATCH_QUEUE_CONCURRENT創(chuàng)建的并發(fā)隊(duì)列 是等價(jià)的
* 第一個(gè)參數(shù):優(yōu)先級(jí) 四種 一般用默認(rèn)的即可
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 最低的優(yōu)先級(jí)
* 第二個(gè)參數(shù):系統(tǒng)說明是給未來使用的,使用的時(shí)候傳參為0即可。
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
創(chuàng)建串行隊(duì)列的方式
1、create創(chuàng)建串行隊(duì)列(主動(dòng)創(chuàng)建)
/*
* 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
* 第二個(gè)參數(shù):隊(duì)列的類型
* DISPATCH_QUEUE_CONCURRENT 并發(fā)
* DISPATCH_QUEUE_SERIAL 串行 或者傳遞NULL
*/
dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_SERIAL);
2、使用主隊(duì)列(跟主線程相關(guān)聯(lián)的隊(duì)列)
/*
* 主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列,放在主隊(duì)列中的任務(wù)都會(huì)放在主線程中進(jìn)行執(zhí)行。
* 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
* 第二個(gè)參數(shù):隊(duì)列的類型
* DISPATCH_QUEUE_CONCURRENT 并發(fā)
* DISPATCH_QUEUE_SERIAL 串行 或者傳遞NULL
*/
函數(shù)和隊(duì)列的組合情況
1、異步函數(shù) + 并發(fā)隊(duì)列: 會(huì)開啟多條線程,隊(duì)列中的任務(wù)是異步(并發(fā))執(zhí)行
// 1.創(chuàng)建隊(duì)列
/*
* 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
* 第二個(gè)參數(shù):隊(duì)列的類型
* DISPATCH_QUEUE_CONCURRENT 并發(fā)
* DISPATCH_QUEUE_SERIAL 串行
*/
dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_CONCURRENT);
// 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
/*
* 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
* 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
* 注意:
并不是有幾個(gè)任務(wù)就開幾條線程,具體開幾條線程由系統(tǒng)進(jìn)行衡量!
*/
dispatch_async(queue, ^{
NSLog(@"1---------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2---------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3---------%@",[NSThread currentThread]);
});
// 打印結(jié)果:
3---------<NSThread: 0x60800007e880>{number = 5, name = (null)}
1---------<NSThread: 0x600000078700>{number = 3, name = (null)}
2---------<NSThread: 0x60800007e2c0>{number = 4, name = (null)}
2、異步函數(shù) + 串行隊(duì)列: 會(huì)開啟一條線程,隊(duì)列中的任務(wù)是同步(串行)執(zhí)行的
// 1.創(chuàng)建隊(duì)列
/*
* 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
* 第二個(gè)參數(shù):隊(duì)列的類型
* DISPATCH_QUEUE_CONCURRENT 并發(fā)
* DISPATCH_QUEUE_SERIAL 串行
*/
dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_SERIAL);
// 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
/*
* 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
* 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
*/
dispatch_async(queue, ^{
NSLog(@"1---------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2---------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3---------%@",[NSThread currentThread]);
});
// 打印結(jié)果:
1---------<NSThread: 0x60000007d180>{number = 3, name = (null)}
2---------<NSThread: 0x60000007d180>{number = 3, name = (null)}
3---------<NSThread: 0x60000007d180>{number = 3, name = (null)}
3、同步函數(shù) + 并發(fā)隊(duì)列:不會(huì)開線程,任務(wù)是串行執(zhí)行的
// 1.創(chuàng)建隊(duì)列
/*
* 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
* 第二個(gè)參數(shù):隊(duì)列的類型
* DISPATCH_QUEUE_CONCURRENT 并發(fā)
* DISPATCH_QUEUE_SERIAL 串行
*/
dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_CONCURRENT);
// 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
/*
* 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
* 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
*/
dispatch_sync(queue, ^{
NSLog(@"1---------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2---------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3---------%@",[NSThread currentThread]);
});
// 打印結(jié)果:
1---------<NSThread: 0x60000007e800>{number = 1, name = main}
2---------<NSThread: 0x60000007e800>{number = 1, name = main}
3---------<NSThread: 0x60000007e800>{number = 1, name = main}
4、同步函數(shù) + 串行隊(duì)列:不會(huì)開線程,任務(wù)是串行執(zhí)行的(效果和3同)
// 1.創(chuàng)建隊(duì)列
/*
* 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
* 第二個(gè)參數(shù):隊(duì)列的類型
* DISPATCH_QUEUE_CONCURRENT 并發(fā)
* DISPATCH_QUEUE_SERIAL 串行
*/
dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_SERIAL);
// 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
/*
* 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
* 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
*/
dispatch_sync(queue, ^{
NSLog(@"1---------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2---------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3---------%@",[NSThread currentThread]);
});
// 打印結(jié)果:
1---------<NSThread: 0x60000007e800>{number = 1, name = main}
2---------<NSThread: 0x60000007e800>{number = 1, name = main}
3---------<NSThread: 0x60000007e800>{number = 1, name = main}
5、異步函數(shù) + 主隊(duì)列:不會(huì)開線程,任務(wù)是串行執(zhí)行的
// 1.獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
/*
* 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
* 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
*/
dispatch_async(queue, ^{
NSLog(@"1---------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2---------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3---------%@",[NSThread currentThread]);
});
// 打印結(jié)果:
1---------<NSThread: 0x60000007e800>{number = 1, name = main}
2---------<NSThread: 0x60000007e800>{number = 1, name = main}
3---------<NSThread: 0x60000007e800>{number = 1, name = main}
6、同步函數(shù)(立刻馬上執(zhí)行) + 主隊(duì)列:死鎖
// 1.獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
/*
* 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
* 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
*/
dispatch_sync(queue, ^{
NSLog(@"1---------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2---------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3---------%@",[NSThread currentThread]);
});
// 說明:
主隊(duì)列的特點(diǎn):如果主隊(duì)列發(fā)現(xiàn)當(dāng)前主線程正在執(zhí)行任務(wù),會(huì)等到主線程執(zhí)行完當(dāng)前任務(wù)后,再調(diào)度主隊(duì)列中的任務(wù),如果在子線程中使用以下方法就可以避免死鎖
同步函數(shù):立刻馬上執(zhí)行,我不執(zhí)行完,后面的誰都別想執(zhí)行
異步函數(shù):如果我不執(zhí)行完,后面的也可以執(zhí)行
各種隊(duì)列執(zhí)行總結(jié)
| 并發(fā)隊(duì)列 | 手動(dòng)創(chuàng)建串行隊(duì)列 | 主隊(duì)列 | |
|---|---|---|---|
| 同步 | 沒有開啟新線程 串行執(zhí)行任務(wù) |
沒有開啟新線程 串行執(zhí)行任務(wù) |
沒有開啟新線程 串行執(zhí)行任務(wù) |
| 異步 | 開啟新線程 并發(fā)執(zhí)行任務(wù) |
開啟新線程 串行執(zhí)行任務(wù) |
沒有開啟新線程 串行執(zhí)行任務(wù) |
七、GCD線程之間的通信
// 1、獲取全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
dispatch_async(queue, ^{
// 在這里執(zhí)行子線程中相關(guān)耗時(shí)的操作
/*
例如網(wǎng)絡(luò)請(qǐng)求或者下載任務(wù)
*/
dispatch_async(dispatch_get_main_queue(), ^{
// 在這里進(jìn)行和主線程的通信,主要是刷新UI的相關(guān)操作,這里用同步函數(shù)也是可以的,并不會(huì)造成死鎖。
});
});
八、GCD常用函數(shù)
1、延遲執(zhí)行
1)延遲執(zhí)行的第一種方法
[self performSelector:@selector(task) withObject:nil afterDelay:2.0];
2)延遲執(zhí)行的第二種方法
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
3)延遲執(zhí)行的第二種方法
/* 主要可以控制延遲執(zhí)行的操作在哪個(gè)隊(duì)列中執(zhí)行 dispatch_get_main_queue() 修改此參數(shù)即可
* 第一個(gè)參數(shù):DISPATCH_TIME_NOW 從現(xiàn)在開始計(jì)算時(shí)間
* 第二個(gè)參數(shù):延遲的時(shí)間 GCD的時(shí)間單位 納秒
* 第三個(gè)參數(shù):dispatch_get_main_queue 主隊(duì)列
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"delay----%@",[NSThread currentThread]);
});
2、一次性執(zhí)行
使用場(chǎng)景:?jiǎn)卫J剑ㄔ谝院蟮姆窒碇袝?huì)提到。。。。。。)
// static 靜態(tài)全局變量,作用域是整個(gè)應(yīng)用程序的生命周期
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"once-----");
});
注意:
一次性代碼不能放在懶加載中 會(huì)出問題
因?yàn)榇舜a只執(zhí)行一次,在第一次創(chuàng)建的時(shí)候會(huì)把對(duì)象返回,但是在第二次創(chuàng)建對(duì)象的時(shí)候,不會(huì)再執(zhí)行一次性代碼,所以對(duì)象返回值為空。
3、GCD的柵欄函數(shù)
控制并發(fā)隊(duì)列中異步執(zhí)行任務(wù)的執(zhí)行順序。
// 獲得全局并發(fā)隊(duì)列
// 柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_queue_t queue = dispatch_queue_create("en", DISPATCH_QUEUE_CONCURRENT);
// 異步函數(shù)
dispatch_async(queue, ^{
NSLog(@"1--------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2--------%@",[NSThread currentThread]);
});
// 柵欄函數(shù) 先執(zhí)行1、2,關(guān)于1、2的先后是不能確定的,再執(zhí)行3。
dispatch_barrier_async(queue, ^{
NSLog(@"+++++++++++++++++++");
});
dispatch_async(queue, ^{
NSLog(@"3--------%@",[NSThread currentThread]);
});
// 打印結(jié)果:
1--------<NSThread: 0x6000000754c0>{number = 3, name = (null)}
2--------<NSThread: 0x60000006a340>{number = 4, name = (null)}
+++++++++++++++++++
3--------<NSThread: 0x60000006a340>{number = 4, name = (null)}
4、GCD快速迭代
迭代其實(shí)就是遍歷,說白了就是一個(gè)循環(huán)。
// 1)快速迭代的第一種方法
for循環(huán),但是for循環(huán)是同步執(zhí)行的。
// 2)快速迭代的第二種方法
/* 主線程和子線程一起完成任務(wù) 任務(wù)的執(zhí)行是并發(fā)的
* 第一個(gè)參數(shù):迭代的次數(shù)
* 第二個(gè)參數(shù):并發(fā)隊(duì)列 只能傳并發(fā)隊(duì)列,如果傳主隊(duì)列,死鎖,如果傳手動(dòng)創(chuàng)建的串行隊(duì)列跟for循環(huán)效果一樣
* 第三個(gè)參數(shù):索引
*/
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%@-----%zu",[NSThread currentThread],index);
});
5、GCD定時(shí)器
/** 定時(shí)器 */
@property (nonatomic, strong) dispatch_source_t timer;
// 創(chuàng)建timer
/* GCD中的定時(shí)器是絕對(duì)精準(zhǔn)的,不會(huì)受到RunLoop的影響!
* 第一個(gè)參數(shù):類型 DISPATCH_SOURCE_TYPE_TIMER
* 第二個(gè)參數(shù):描述信息
* 第三個(gè)參數(shù):更詳細(xì)的描述信息
* 第四個(gè)參數(shù):隊(duì)列 決定GCD的timer在那個(gè)線程中執(zhí)行
*/
// 以局部變量的形式進(jìn)行定義,timer不會(huì)執(zhí)行的,因?yàn)樵趫?zhí)行之前已經(jīng)被釋放了
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
/*
* 第一個(gè)參數(shù):定時(shí)器對(duì)象
* 第二個(gè)參數(shù):DISPATCH_TIME_NOW 起始時(shí)間
* 第三個(gè)參數(shù):間隔時(shí)間
* 第四個(gè)參數(shù):精準(zhǔn)度 允許誤差,此處傳0即可
*/
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
// 設(shè)置定時(shí)器的任務(wù)
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"執(zhí)行任務(wù)");
});
// 開啟定時(shí)器
dispatch_resume(self.timer);
九、GCD隊(duì)列組的使用
跟柵欄函數(shù)相類似,也是用來控制并發(fā)隊(duì)列中異步執(zhí)行任務(wù)的執(zhí)行順序的。
// 1、獲得全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2、創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 3、異步函數(shù)
/* 這個(gè)函數(shù)做了什么?
1、封裝任務(wù)
2、將任務(wù)添加到隊(duì)列中
3、會(huì)監(jiān)聽任務(wù)執(zhí)行情況,通知group
*/
dispatch_group_async(group, queue, ^{
NSLog(@"1---------%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"2---------%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"3---------%@",[NSThread currentThread]);
});
// 4、攔截通知,當(dāng)所有的隊(duì)列組中的任務(wù)執(zhí)行完畢以后會(huì)執(zhí)行這個(gè)方法
dispatch_group_notify(group, queue, ^{
NSLog(@"end--------end");
});
// 打印結(jié)果:
2---------<NSThread: 0x60800007a8c0>{number = 7, name = (null)}
1---------<NSThread: 0x60800007a800>{number = 6, name = (null)}
3---------<NSThread: 0x608000078980>{number = 8, name = (null)}
end--------end
另外一種GCD隊(duì)列組的實(shí)現(xiàn)方式(以前使用的)
// 獲得全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 此方法之后執(zhí)行的執(zhí)行的異步任務(wù)會(huì)被納入到隊(duì)列組的監(jiān)聽范圍內(nèi)
// 配對(duì)使用
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"1---------%@",[NSThread currentThread]);
// 離開群組
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"2---------%@",[NSThread currentThread]);
// 離開群組
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"3---------%@",[NSThread currentThread]);
// 離開群組
dispatch_group_leave(group);
});
// 當(dāng)所有的隊(duì)列組中的任務(wù)執(zhí)行完畢以后會(huì)執(zhí)行這個(gè)方法
// 內(nèi)部本身是異步的,不會(huì)阻塞
dispatch_group_notify(group, queue, ^{
NSLog(@"end--------end");
});
// 其他的監(jiān)聽方法 DISPATCH_TIME_FOREVER 死等 等到所有任務(wù)都執(zhí)行完畢才執(zhí)行這個(gè)方法
// 這里是阻塞的,如果下面方法沒有執(zhí)行,以后的方法不會(huì)執(zhí)行的。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
十、GCD異步函數(shù)的補(bǔ)充
GCD異步函數(shù)有兩種實(shí)現(xiàn)方式,具體的異同比較如下:
1、block 實(shí)現(xiàn)
// dispatch_async(<dispatch_queue_t _Nonnull queue>, <^(void)block>)
2、方法調(diào)用的方式實(shí)現(xiàn)
/*
* 第一個(gè)參數(shù):隊(duì)列
* 第二個(gè)參數(shù):參數(shù)
* 第三個(gè)參數(shù):要調(diào)用的函數(shù)名稱
*/
dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
// 調(diào)用函數(shù)的實(shí)現(xiàn)
void task(void *params){
NSLog(@"%s--------%@",__func__,[NSThread currentThread]);
}