概念解釋
1. 執(zhí)行任務(wù)的函數(shù):在GCD中介陶,任務(wù)是通過 block來封裝的,并且任務(wù)的block沒有參數(shù)也沒有返回值。
同步:你必須把我的代碼執(zhí)行完你再走山析,一定要執(zhí)行完同步里的代碼再執(zhí)行下面的代碼
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
異步:你先走執(zhí)行我下面的代碼遥椿,我找人基矮、找線程去執(zhí)行我里面的代碼
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
2. GCD使用步驟:
第一步: 創(chuàng)建/獲取 隊列
第二步: 創(chuàng)建任務(wù),確定要做的事情
第三步: 將任務(wù)添加到隊列中
(1)GCD會自動將隊列中的任務(wù)取出冠场,放到對應(yīng)的線程中執(zhí)行
(2)任務(wù)的取出遵循隊列的FIFO原則: 先進(jìn)先出家浇,后進(jìn)后出
3. 隊列
包括: 串行隊列、并發(fā)隊列碴裙、主隊列钢悲、全局隊列
1. 串行隊列(Serial Dispatch Queue)
串行隊列的特點:
以先進(jìn)先出的方式,按順序調(diào)度隊列中的任務(wù)去執(zhí)行莺琳,一次只能調(diào)度一個任務(wù)。
無論隊列中所指定的執(zhí)行任務(wù)的函數(shù)是同步還是異步诱贿,都必須等待前一個任務(wù)執(zhí)行完畢,才可以調(diào)度后面的任務(wù)咕缎。
串行隊列的創(chuàng)建:
(1) dispatch_queue_tqueue = dispatch_queue_create("itheima", DISPATCH_QUEUE_SERIAL);
(2) dispatch_queue_tqueue = dispatch_queue_create("itheima", NULL);
串行隊列珠十,同步執(zhí)行:
開不開線程? 不開線程焙蹭。
順序執(zhí)行還是亂序執(zhí)行? 順序執(zhí)行。
串行隊列嫂伞,異步執(zhí)行:
開不開線程? 只會開一條線程孔厉。
順序執(zhí)行還是亂序執(zhí)行? 順序執(zhí)行。
2. 并發(fā)隊列(Concurrent Dispatch Queue)
- 并發(fā)隊列的特點:以先進(jìn)先出的方式撰豺,并發(fā)(同時)調(diào)度隊列中的任務(wù)去執(zhí)行拼余。
- 如果當(dāng)前調(diào)度的任務(wù)是同步執(zhí)行的,會等待當(dāng)前任務(wù)執(zhí)行完畢后凡橱,再調(diào)度后續(xù)的任務(wù)亭姥。
- 如果當(dāng)前調(diào)度的任務(wù)是異步執(zhí)行的,同時底層線程池有可用的線程資源坝撑,就不會等待當(dāng)前任務(wù)粮揉,直接調(diào)度任務(wù)到新線程去執(zhí)行。
并發(fā)隊列的創(chuàng)建:
dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);
并發(fā)隊列,同步執(zhí)行:
開不開線程? 不開線程塔沃。
順序執(zhí)行還是亂序執(zhí)行? 順序執(zhí)行。
并發(fā)同步 和 串行同步的執(zhí)行結(jié)果一模一樣螃概。
并發(fā)隊列吊洼,異步執(zhí)行:
開不開線程? 開多條新線程。
順序執(zhí)行還是亂序執(zhí)行? 亂序執(zhí)行冒窍。
3. 主隊列:
- 遇到主隊列,不管同步異步都要先執(zhí)行完主線程里的代碼再執(zhí)行主隊列里的代碼
- dispatch_sync方法不能在主隊列中調(diào)用款慨,因為這會無限期的阻止線程并會導(dǎo)致你的應(yīng)用死鎖谬莹。所有通過GCD提交到主隊列的任務(wù)必須是異步的附帽。
- 只有當(dāng)主線程空閑時, 主隊列才會調(diào)度任務(wù)到主線程執(zhí)行
- 主隊列是系統(tǒng)提供的,無需自己創(chuàng)建整胃,可以直接通過dispatch_get_main_queue()函數(shù)來獲取慢显。
- 主隊列的特點:先執(zhí)行完主線程上的代碼,才會執(zhí)行主隊列中的任務(wù)
1屋灌、添加到主隊列的任務(wù)只能由主線程來執(zhí)行应狱。
2疾呻、以先進(jìn)先出的方式,只有當(dāng)主線程的代碼執(zhí)行完畢后尉咕,主隊列才會調(diào)度任務(wù)到主線程執(zhí)行璃岳。
如下列代碼執(zhí)行打印123后再打印hello
- (void)demo1 {
for (int i = 0; i < 10; i++) {
NSLog(@"111");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"hello %d %@",i,[NSThread currentThread]);
});
NSLog(@"222");
NSLog(@"333");
}
}
- 主隊列悔捶,同步執(zhí)行(死鎖)主線程和主隊列同步任務(wù)相互等待蜕该,造成死鎖洲鸠。
執(zhí)行到同步主隊列后,同步時主線程想讓里面代碼順序執(zhí)行下去(直接進(jìn)去執(zhí)行里面代碼)绢淀,而主隊列要求執(zhí)行完畢主線程代碼再執(zhí)行里面代碼袜匿,導(dǎo)致誰都無法繼續(xù)執(zhí)行居灯,導(dǎo)致死鎖。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"begin");
for (int i = 0; i < 10; i++) { //死鎖义锥,一看同步就不分子線程了岩灭,一看主隊列,就等著主線程執(zhí)行完來執(zhí)行里面代碼
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"hello %d %@",i,[NSThread currentThread]);
}); }
NSLog(@"end");
}
- 解決死鎖的問題柱恤,全局隊列異步執(zhí)行找爱,里面嵌套主隊列同步執(zhí)行
當(dāng)我們將主隊列同步執(zhí)行任務(wù)放到子線程去執(zhí)行车摄,就不會出現(xiàn)死鎖。
由于將主隊列同步放到了子線程中執(zhí)行变屁,主隊列同步任務(wù)無法阻塞主線程執(zhí)行代碼意狠,因此主線程可以將主線程上的代碼執(zhí)行完畢。當(dāng)主線程執(zhí)行完畢之后闷板,就會執(zhí)行主隊列里面的任務(wù)。
先執(zhí)行begin和end,再順序執(zhí)行全局隊列異步執(zhí)行(第二個線程里執(zhí)行)
- (void)demo3 {
NSLog(@"begin");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 10; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"hello %d %@",i,[NSThread currentThread]); }); }
});
NSLog(@"end");
}
主隊列鹏漆,異步執(zhí)行
執(zhí)行到異步主隊列后创泄,里面代碼異步執(zhí)行后釋放(不創(chuàng)建新線程)鞠抑,主線程執(zhí)行完畢后回來調(diào)用主隊列中代碼開不開線程? 主隊列,就算是異步執(zhí)行秒梳,也不會開線程箕速。
順序執(zhí)行還是亂序執(zhí)行? 順序執(zhí)行。
先把主線程上的代碼執(zhí)行完畢兴垦,才會執(zhí)行添加到主隊列里面的任務(wù)探越。
主隊列和串行隊列的區(qū)別
串行隊列:必須等待一個任務(wù)執(zhí)行完畢窑业,才會調(diào)度下一個任務(wù),順序執(zhí)行代碼节槐。
主隊列:如果主線程上有代碼執(zhí)行拐纱,主隊列就不調(diào)度任務(wù)秸架,跳過主隊列代碼執(zhí)行主線程代碼,完畢后再執(zhí)行主隊列內(nèi)代碼蚂子。
4. 全局隊列:
全局隊列是系統(tǒng)提供的,無需自己創(chuàng)建蒂破,可以直接通過dispatch_get_global_queue(long identifier, unsigned long flags);函數(shù)來獲取别渔。
全局隊列的工作特性跟并發(fā)隊列一致。 實際上喇伯,全局隊列就是系統(tǒng)為了方便程序員拨与,專門提供的一種特殊的并發(fā)隊列买喧。
全局隊列和并發(fā)隊列的區(qū)別
全局隊列:沒有名稱,無論ARC還是MRC都不需要考慮內(nèi)存釋放秋度,日常開發(fā)钱床,建議使用全局隊列
并發(fā)隊列:
(1)有名稱,如果在MRC開發(fā)中事期,需要使用dispatch_release來釋放相應(yīng)的對象
(2)dispatch_barrier 必須使用自定義的并發(fā)隊列
(3)開發(fā)第三方框架兽泣,建議使用并發(fā)隊列第一個參數(shù): identifier
iOS7.0胁孙,表示的是優(yōu)先級:
DISPATCH_QUEUE_PRIORITY_HIGH = 2; 高優(yōu)先級
DISPATCH_QUEUE_PRIORITY_DEFAULT = 0; 默認(rèn)優(yōu)先級
DISPATCH_QUEUE_PRIORITY_LOW = -2; 低優(yōu)先級
DISPATCH_QUEUE_PRIORITY_BACKGROUND = INT16_MIN; 后臺優(yōu)先級
iOS8.0開始涮较,推薦使用服務(wù)質(zhì)量(QOS):
QOS_CLASS_USER_INTERACTIVE = 0x21; 用戶交互
QOS_CLASS_USER_INITIATED = 0x19; 用戶期望
QOS_CLASS_DEFAULT = 0x15; 默認(rèn)
QOS_CLASS_UTILITY = 0x11; 實用工具
QOS_CLASS_BACKGROUND = 0x09; 后臺
QOS_CLASS_UNSPECIFIED = 0x00; 未指定
通過對比可知: 第一個參數(shù)傳入0,可以同時適配iOS7及iOS7以后的版本候齿。
服務(wù)質(zhì)量和優(yōu)先級是一一對應(yīng)的:
DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND
- 第二個參數(shù): flags 為未來保留使用的,始終傳入0周霉。
方法:
- 獲取系統(tǒng)隊列
(1)獲取主隊列(一種串行隊列) dispatch_queue_t 類型
dispatch_get_main_queue()
(2)獲取全局隊列(一種并發(fā)隊列) dispatch_queue_t 類型
dispatch_get_global_queue(0, 0)
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
- 自定義創(chuàng)建一個串行俱箱、并發(fā)隊列
參數(shù)1:隊列名
參數(shù)2:隊列類型灭必,串行還是并發(fā)隊列
串行隊列:DISPATCH_QUEUE_SERIAL 或 NULL
并發(fā)隊列:DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
例子:創(chuàng)建一個串行隊列
dispatch_queue_t queue = dispatch_queue_create("hm", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("itheima", NULL);
- 創(chuàng)建任務(wù)
typedef void (^dispatch_block_t)(void);
例子:
dispatch_block_t task = ^{
NSLog(@"hello %@",[NSThread currentThread]);
};
- 將任務(wù)添加到隊列(參數(shù)1:隊列 參數(shù)2:任務(wù))
- 同步函數(shù)厂财,任務(wù)會在當(dāng)前線程執(zhí)行峡懈,因為同步函數(shù)不具備開新線程的能力肪康。
同步:你必須把我的代碼執(zhí)行完你再走,一定要執(zhí)行完同步里的代碼再執(zhí)行下面的代碼
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- 異步函數(shù)谒撼,任務(wù)會在子線程執(zhí)行灶伊,因為異步函數(shù)具備開新線程的能力。
異步:你先走執(zhí)行我下面的代碼,我找人移盆、找線程去執(zhí)行我里面的代碼
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
例子:將任務(wù)同步添加到隊列和將任務(wù)異步添加到隊列
(1)dispatch_sync(queue, task);
(2)dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"hello %@",[NSThread currentThread]);
});
- Barrier阻塞
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
- 概念:會阻塞dispatch_barrier_async代碼里的任務(wù)咒循,讓其它任務(wù)先執(zhí)行完畢后再去執(zhí)行里面的代碼
- 用處:
(1)適合于大規(guī)模的 I/O 操作
(2)當(dāng)訪問數(shù)據(jù)庫或文件的時候叙甸,更新數(shù)據(jù)的時候不能和其他更新或讀取的操作在同一時間執(zhí)行,可以使用調(diào)度組不過有點復(fù)雜便脊。可以使用dispatch_barrier_async解決遂赠。 - 例子:執(zhí)行結(jié)果十個圖片下載完成先執(zhí)行完畢晌杰,再執(zhí)行十個保存圖片
_queue = dispatch_queue_create("110", DISPATCH_QUEUE_CONCURRENT);
for (int i = 1; i<=10; i++) {
[self downloadImage:i];}
-(void)downloadImage:(int)index {
dispatch_async(_queue, ^{ //模擬下載圖片
NSString *fileName = [NSString stringWithFormat:@"%02d.jpg",index % 10 + 1];
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
UIImage *img = [UIImage imageWithContentsOfFile:path];
//等待隊列中所有的任務(wù)執(zhí)行完成(十個圖片下載完成任務(wù))肋演,才會執(zhí)行barrier中的代碼
dispatch_barrier_async(_queue, ^{
[self.photoList addObject:img];
NSLog(@"保存圖片 %@ %@",fileName,[NSThread currentThread]);
});
NSLog(@"圖片下載完成 %@ %@",fileName,[NSThread currentThread]);
});
}
- 延時操作
void dispatch_after(dispatch_time_t when,
dispatch_queue_t queue,
dispatch_block_t block);
- 參數(shù)1:
dispatch_time_t when
多少納秒之后執(zhí)行
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);
#define DISPATCH_TIME_NOW (0ull)
#define DISPATCH_TIME_FOREVER (~0ull)
#define NSEC_PER_SEC 1000000000ull#define NSEC_PER_MSEC 1000000ull#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
- 參數(shù)2:
dispatch_queue_t queue
任務(wù)添加到哪個隊列 - 參數(shù)3:
dispatch_block_t block
要執(zhí)行的任務(wù)
用法:延遲1秒后在主隊列執(zhí)行一個任務(wù)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
- 一次性執(zhí)行
- 注意:一次性執(zhí)行是線程安全的爹殊,只在當(dāng)前線程上一次性執(zhí)行
- 步驟:
(1)創(chuàng)建一個靜態(tài)的 dispatch_once_t 變量梗夸;
(2) dispatch_once(變量名, ^{
//一次性執(zhí)行語句
}); - 原理:判斷靜態(tài)的全局變量的值,默認(rèn)是0辛块,執(zhí)行完成后變?yōu)?1
dispatch_once內(nèi)部會判斷變量的值铅碍,如果是0才執(zhí)行 - 用處:可以使用一次性執(zhí)行創(chuàng)建單例對象,效率比互斥鎖高
- 例子:
for (int i = 0; i<20000; i++) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"hello %@",[NSThread currentThread]); });
}
- 調(diào)度組
- 用處:有時候需要在多個異步任務(wù)都執(zhí)行完成之后繼續(xù)做某些事情尘盼,比如下載歌曲悔叽,等所有的歌曲都下載完畢之后再轉(zhuǎn)到主線程提示用戶
- 步驟:
(1)創(chuàng)建隊列
(2)創(chuàng)建調(diào)度組
dispatch_group_t group = dispatch_group_create();
(3)監(jiān)聽調(diào)度組內(nèi)隊列任務(wù)是否執(zhí)行完畢:把任務(wù)添加到隊列中爵嗅,隊列添加到調(diào)度組中,隊列任務(wù)執(zhí)行完畢通知調(diào)度組
void dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
(4)接收到調(diào)度組執(zhí)行完畢的通知后睹晒,執(zhí)行其它任務(wù)
void dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
例子:
//創(chuàng)建組
dispatch_group_t group = dispatch_group_create();
//隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//下載第一首歌曲
dispatch_group_async(group, queue, ^{
NSLog(@"正在下載第一個歌曲");
});
//下載第二首歌曲
dispatch_group_async(group, queue, ^{
NSLog(@"正在下載第二個歌曲");
[NSThread sleepForTimeInterval:2.0];
});//下載第三首歌曲dispatch_group_async(group, queue, ^{ NSLog(@"正在下載第三個歌曲");
});
//當(dāng)三個異步任務(wù)都執(zhí)行完成伪很,才執(zhí)行dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"over %@",[NSThread currentThread]);
});