一.進(jìn)程&線程
- 進(jìn)程:是程序執(zhí)行過程中
分配和管理資源
的一個(gè)基本單位。 - 線程:是程序執(zhí)行過程中
任務(wù)調(diào)度和執(zhí)行
的一個(gè)基本單位。 - 一個(gè)進(jìn)程里面有多個(gè)線程,
線程是進(jìn)程的一部分
峡继。
二. 任務(wù)
任務(wù):就是要執(zhí)行什么操作。
1. 同步執(zhí)行(sync
):
- 在當(dāng)前的線程上面執(zhí)行任務(wù)匈挖。
- 不具備開啟新線程的能力碾牌。
2. 異步執(zhí)行(async
):
- 可以開啟新的線程執(zhí)行任務(wù)。
- 具備開啟新線程的能力关划。
三. 隊(duì)列
隊(duì)列:用于存放任務(wù)小染,遵循FIFO(先進(jìn)先出)的原則。
1. 串行隊(duì)列(serial
):
- 隊(duì)列中每次只執(zhí)行一個(gè)任務(wù)贮折,當(dāng)?shù)谝粋€(gè)執(zhí)行完才能執(zhí)行第二個(gè)裤翩。
- 只能開啟一個(gè)線程。
2. 并發(fā)隊(duì)列(concurrent
):
- 隊(duì)列中可以同時(shí)執(zhí)行多個(gè)任務(wù)调榄。
- 可以開啟多個(gè)線程踊赠。
3. 兩種特殊隊(duì)列:
- 全局隊(duì)列(
dispatch_get_global_queue
):直接作為普通并發(fā)隊(duì)列使用。 - 主隊(duì)列(
dispatch_get_main_queue
):任務(wù)在主線程中執(zhí)行的串行隊(duì)列每庆。
四. 使用步驟
注:把任務(wù)
放到...(主隊(duì)列 & 串行隊(duì)列 & 全局隊(duì)列
)隊(duì)列中...(同步 & 異步
)執(zhí)行筐带。
1. 創(chuàng)建隊(duì)列
- 串行隊(duì)列 & 并發(fā)隊(duì)列
// 串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("com.wxh.queue", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊(duì)列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.wxh.queue", DISPATCH_QUEUE_CONCURRENT);
注:第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識(shí)符
,DEBUG
的時(shí)候用的缤灵,可以為空伦籍,推薦使用類似于APP的BundleID
這種逆序域名;第二個(gè)參數(shù)識(shí)別是串行隊(duì)列
還是并發(fā)隊(duì)列
腮出,串行隊(duì)列
用DISPATCH_QUEUE_SERIAL
帖鸦,并發(fā)隊(duì)列用DISPATCH_QUEUE_CONCURRENT
。
- 主隊(duì)列 & 全局隊(duì)列
// 主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 全局隊(duì)列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
注:主隊(duì)列不用傳參數(shù)胚嘲。全局隊(duì)列第一個(gè)參數(shù)一般用DISPATCH_QUEUE_PRIORITY_DEFAULT
作儿,表示優(yōu)先級(jí)的。第二個(gè)參數(shù)暫時(shí)沒用馋劈,用0
即可攻锰。
2. 創(chuàng)建任務(wù)
// 同步任務(wù)創(chuàng)建
dispatch_sync(queue, ^{
});
// 異步任務(wù)創(chuàng)建
dispatch_async(queue, ^{
});
注:參數(shù)queue
就是隊(duì)列的類型。
3. 小結(jié)
-
并發(fā)隊(duì)列
和全局隊(duì)列
使用場(chǎng)景是一致的妓雾,通常都是使用全局隊(duì)列
娶吞。 - 總共有
3
種隊(duì)列:全局隊(duì)列
,主隊(duì)列
君珠,串行隊(duì)列
寝志;有2
種任務(wù):同步任務(wù)
,異步任務(wù)
策添。 - 共有
6
種使用方式材部,能否開啟新的線程有同步
或者異步
決定,但是否開啟新的線程要看當(dāng)前狀況是否需要
開啟新的線程來決定唯竹。 -
sync
會(huì)照成阻塞
現(xiàn)象乐导,sync
任務(wù)下的隊(duì)列里面的任務(wù)要必須完成一個(gè)才能繼續(xù)下一個(gè)。 -
sync
阻塞的是隊(duì)列浸颓,不是線程物臂。
五. 使用
1. 同步主隊(duì)列 syncMain
解讀:把任務(wù)
放到主隊(duì)列
中同步執(zhí)行
。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)1");
NSLog(@"任務(wù)2");
});
如果在主線程中執(zhí)行
syncMain
产上,會(huì)出現(xiàn)死鎖
現(xiàn)象棵磷。
解釋:在主線程上面執(zhí)行同步主隊(duì)列syncMain
任務(wù),相當(dāng)于把syncMain
任務(wù)加到了主隊(duì)列Main
中晋涣。當(dāng)要執(zhí)行任務(wù)1
的時(shí)候仪媒,也要先把任務(wù)1
加到Main
中。因?yàn)槭峭饺蝿?wù)sync
谢鹊,所以要執(zhí)行任務(wù)1
之前算吩,必須先等待syncMain
任務(wù)完成。但是要完成syncMain
任務(wù)前佃扼,又必須執(zhí)行任務(wù)1
偎巢,這時(shí)候syncMain
任務(wù)和任務(wù)1
之間就會(huì)互相等待,出現(xiàn)死鎖
現(xiàn)象兼耀,程序崩潰压昼。如果是在子線程中使用
syncMain
,不開啟新的線程瘤运。先完成任務(wù)1
窍霞,再完成任務(wù)2
。
解釋:因?yàn)槭峭饺蝿?wù)sync
尽超,不具備開啟新的線程的能力官撼。在子線程中執(zhí)行syncMain
,相當(dāng)于吧syncMain
任務(wù)加到了子線程的隊(duì)列(這里用隊(duì)列A
表示)中似谁。當(dāng)要執(zhí)行任務(wù)1
和任務(wù)2
的時(shí)候傲绣,把任務(wù)1
和任務(wù)2
加到主隊(duì)列Main
中,這個(gè)時(shí)候巩踏,Main
中是沒有其他任務(wù)的秃诵,所以不會(huì)出現(xiàn)死鎖
現(xiàn)象。Main
是特殊的串行隊(duì)列塞琼,所以先執(zhí)行完任務(wù)1
菠净,再執(zhí)行任務(wù)2
。
2. 異步主隊(duì)列 asyncMain
解讀:把任務(wù)
放到主隊(duì)列
中異步執(zhí)行
。
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)1");
NSLog(@"任務(wù)2");
});
不開啟新的線程毅往。先完成任務(wù)1
牵咙,再完成任務(wù)2
。
解釋:雖然是異步任務(wù)async
攀唯,具備開啟新的線程的能力洁桌。但是由于是在主隊(duì)列Main
中執(zhí)行任務(wù),Main
中的任務(wù)必須在主線程完成侯嘀,所以不需要開啟新的線程另凌。在主線程中執(zhí)行asyncMain
任務(wù),相當(dāng)于把asyncMain
任務(wù)加到主隊(duì)列Main
中戒幔。當(dāng)要執(zhí)行任務(wù)1
的時(shí)候吠谢,也要任務(wù)1
加到Main
中。因?yàn)槭钱惒饺蝿?wù)async
诗茎,所以asyncMain
任務(wù)可以先等待工坊,先執(zhí)行完任務(wù)1
,再執(zhí)行任務(wù)2
错沃,所以不會(huì)出現(xiàn)死鎖
現(xiàn)象栅组;在子線程執(zhí)行asyncMain
任務(wù),跟子線程執(zhí)行syncMain
任務(wù)同等邏輯枢析。
使用場(chǎng)景:做網(wǎng)絡(luò)請(qǐng)求從后臺(tái)接口獲取到數(shù)據(jù)之后玉掸,需要根據(jù)數(shù)據(jù)更新界面UI,一般都是用asyncMain
醒叁,在asyncMain
的block
里面執(zhí)行刷新界面的操作司浪。
3.同步串行隊(duì)列 syncSerial
解讀:把任務(wù)
放到串行隊(duì)列
中同步執(zhí)行
。
dispatch_sync(dispatch_queue_create("com.wxh.queue", DISPATCH_QUEUE_SERIAL), ^{
NSLog(@"任務(wù)1");
NSLog(@"任務(wù)2");
});
不會(huì)開啟新的線程把沼。先完成任務(wù)1
啊易,再完成任務(wù)2
。
解釋:因?yàn)槭峭饺蝿?wù)sync
饮睬,sync
不具備開啟新的線程租谈。執(zhí)行syncSerial
任務(wù)時(shí),syncSerial
任務(wù)存放在當(dāng)前線程的隊(duì)列(這里用隊(duì)列B
表示)中捆愁,執(zhí)行任務(wù)1
的時(shí)候割去,將任務(wù)1
放到當(dāng)前線程的串行隊(duì)列Serial
中,任務(wù)2
也一樣昼丑。因?yàn)槭?code>Serial呻逆,所以要先執(zhí)行完任務(wù)1
,再執(zhí)行任務(wù)2
菩帝。
問題:同步串行隊(duì)列syncSerial
在主線程上執(zhí)行咖城,為什么不會(huì)出現(xiàn)死鎖
現(xiàn)象茬腿?
回答:在主線程上執(zhí)行syncSerial
任務(wù),syncSerial
任務(wù)存放在主隊(duì)列Main
當(dāng)中宜雀,而任務(wù)1
和任務(wù)2
都放在主線程的串行隊(duì)列Serial
中(不是在主隊(duì)列Main
中哦~)切平。此時(shí),主線程上面有兩個(gè)隊(duì)列州袒,一個(gè)是存放syncSerial
任務(wù)的Main
揭绑,另一個(gè)是存放任務(wù)1
和任務(wù)2
的Serial
弓候。當(dāng)執(zhí)行syncSerial
任務(wù)中的任務(wù)1
時(shí)郎哭,會(huì)從主隊(duì)列Main
去到串行隊(duì)列Serial
,然后在Serial
繼續(xù)執(zhí)行任務(wù)2
菇存,執(zhí)行完任務(wù)2
夸研,回到主隊(duì)列Main
中完成syncSerial
任務(wù)。
4.異步串行隊(duì)列 asyncSerial
解讀:把任務(wù)
放到串行隊(duì)列
中異步執(zhí)行
依鸥。
dispatch_async(dispatch_queue_create("com.wxh.queue", DISPATCH_QUEUE_SERIAL), ^{
NSLog(@"任務(wù)1");
NSLog(@"任務(wù)2");
});
會(huì)開啟一條新的線程亥至。先完成任務(wù)1
,再完成任務(wù)2
贱迟。
解釋:因?yàn)槭钱惒饺蝿?wù)async
姐扮,具備開啟新的線程的能力。因?yàn)槭窃诖嘘?duì)列Serial
中衣吠,只能同時(shí)執(zhí)行一個(gè)任務(wù)茶敏,所以只需要開啟一條新的線程。asyncSerial
任務(wù)存放在當(dāng)前線程的隊(duì)列中(這里用隊(duì)列C
表示)缚俏,而任務(wù)1
和任務(wù)2
存放在新開啟的線程的Serial
惊搏。當(dāng)執(zhí)行asyncSerial
任務(wù)宝恶,要開始執(zhí)行任務(wù)1
時(shí)鞠评,先去到新開啟的線程的Serial
中魄衅,執(zhí)行完任務(wù)1
,再執(zhí)行任務(wù)2
常空,然后回到隊(duì)列C
中完成asyncSerial
任務(wù)屠阻。
5. 同步全局隊(duì)列 syncGlobal
解讀:把任務(wù)
放到全局隊(duì)列
中同步執(zhí)行
郁岩。
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任務(wù)1");
NSLog(@"任務(wù)2");
});
不會(huì)開啟新的線程铡恕。先完成任務(wù)1
煮嫌,再完成任務(wù)2
刹缝。
解釋:因?yàn)槭峭饺蝿?wù)sync
碗暗,不具備開啟新的線程能力。雖然是在全局隊(duì)列Global
中赞草,可以多個(gè)任務(wù)同時(shí)進(jìn)行讹堤,但是只有一條線程,所以還是要先完成任務(wù)1
再執(zhí)行任務(wù)2
厨疙。syncGlobal
任務(wù)存放在當(dāng)前線程的隊(duì)列(這里用隊(duì)列D
表示)中洲守,執(zhí)行任務(wù)1
的時(shí)候疑务,將任務(wù)1
放到當(dāng)前線程的全局隊(duì)列Global
中,任務(wù)2
也一樣梗醇。雖然是Global
知允,但是只有一條線程,所以要先執(zhí)行完任務(wù)1
叙谨,再執(zhí)行任務(wù)2
温鸽。
使用場(chǎng)景:上傳多張圖片到后臺(tái),后臺(tái)要求一張一張的上傳手负。
6. 異步全局隊(duì)列 asyncGlobal
解讀:把任務(wù)
放到全局隊(duì)列
中異步執(zhí)行
涤垫。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任務(wù)1");
NSLog(@"任務(wù)2");
});
會(huì)開啟多條新的線程。任務(wù)1
和任務(wù)2
同時(shí)執(zhí)行竟终。
解釋:因?yàn)槭钱惒饺蝿?wù)async
蝠猬,async
會(huì)具備開啟新的線程能力。因?yàn)槭侨株?duì)列Global
统捶,所以Global
里面的任務(wù)可以同時(shí)執(zhí)行榆芦。asyncGlobal
任務(wù)存放在當(dāng)前線程的隊(duì)列(這里用隊(duì)列E
表示)中,而任務(wù)1
和任務(wù)2
存放在各自開啟的線程隊(duì)列中喘鸟。當(dāng)執(zhí)行asyncGlobal
任務(wù)匆绣,因?yàn)槭?code>Global,所以任務(wù)1
和任務(wù)2
可以同時(shí)執(zhí)行什黑。
使用場(chǎng)景:上傳多張圖片到后臺(tái)崎淳,可以多張同時(shí)上傳。
六.GCD線程之間的通信
異步開啟子線程執(zhí)行耗時(shí)任務(wù)兑凿,耗時(shí)任務(wù)完成凯力,利用主隊(duì)列回到主線程更新UI。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 10; i ++) {
NSLog(@"---%d",i);
// 模擬耗時(shí)操作
[NSThread sleepForTimeInterval:1];
}
});
dispatch_async(dispatch_get_main_queue(), ^{
// 耗時(shí)操作完成
NSLog(@"任務(wù)完成礼华,回到主線程更新UI咐鹤。");
});
});
七.阻塞方法dispatch_barrier
作用:在有多個(gè)任務(wù)并且使用柵欄方法dispatch_barrier
的隊(duì)列(注意:不能使用全局隊(duì)列
),必須先等待dispatch_barrier
前面的任務(wù)執(zhí)行完畢圣絮,才能執(zhí)行dispatch_barrier
里面的任務(wù)祈惶。等待dispatch_barrier
里面的任務(wù)執(zhí)行完畢,才能繼續(xù)執(zhí)行dispatch_barrier
之后的任務(wù)扮匠。
例子:有三種圖片捧请,分別壓縮之后,一起上傳后后臺(tái)棒搜。
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.wxh.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"壓縮圖片1");
});
dispatch_async(concurrentQueue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"壓縮圖片2");
});
dispatch_async(concurrentQueue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"壓縮圖片3");
});
dispatch_barrier_async(concurrentQueue, ^{
NSLog(@"將 壓縮圖片1 壓縮圖片2 壓縮圖片3 上傳到后臺(tái)");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"其他操作");
});
結(jié)果:這樣子既能讓3張圖片同時(shí)壓縮疹蛉,又能確保3張圖片都?jí)嚎s完之后,才將3張圖片上傳到后臺(tái)力麸。
問題1:為什么不能使用 全局隊(duì)列 可款?
解釋:蘋果官方給的說明是如果使用全局隊(duì)列育韩,那么dispatch_barrier_async
方法將退化成dispatch_async
方法。個(gè)人覺得闺鲸,不知道對(duì)不對(duì)筋讨,全局隊(duì)列沒有名字,自定義的并發(fā)隊(duì)列是有名字的摸恍,系統(tǒng)需要重新控制隊(duì)列里面任務(wù)的執(zhí)行操作悉罕,必須具體到哪個(gè)隊(duì)列中去重新控制。
問題2:dispatch_barrier_asyn
和dispatch_barrier_syn
的區(qū)別立镶?
解釋:上例中壁袄,將dispatch_barrier_asyn
替換成dispatch_barrier_syn
效果是一樣的。它們的區(qū)別在于谜慌,
-
dispatch_barrier_asyn
將自己的任務(wù)加入到隊(duì)列中之后然想,不用等自己的任務(wù)執(zhí)行完畢,它就將它后面的任務(wù)也加入到隊(duì)列中欣范。然后等待自己的任務(wù)執(zhí)行完畢,才執(zhí)行后面的任務(wù)令哟。 -
dispatch_barrier_syn
將自己的任務(wù)加入到隊(duì)列中之后恼琼,需要等待自己的任務(wù)執(zhí)行完畢,才能加入后臺(tái)的任務(wù)屏富,并執(zhí)行后面的任務(wù)晴竞。
八.延時(shí)方法 dispatch_after
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
方法中需要傳入一個(gè)延時(shí)的時(shí)間(秒),延時(shí)操作里面的任務(wù)放到主隊(duì)列
執(zhí)行狠半。
九.只執(zhí)行一次(單例) dispatch_once
+ (instancetype)shareInstance{
static Singleton *single;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
single = [[Singleton alloc] init];
});
return single;
}
在程序運(yùn)行過程中噩死,dispatch_once
方法中的代碼只會(huì)被執(zhí)行1次,即不影響性能神年,又能保證線程安全已维。
-
原理:
dispatch_once
方法是根據(jù)dispatch_once_t
修飾的變量onceToken
的值來決定接下來的操作的。
(1)當(dāng)onceToken = 0
時(shí)已日,說明程序第一次執(zhí)行dispatch_once
方法垛耳,直接執(zhí)行dispatch_once
的block
中的代碼。
(2)當(dāng)onceToken = -1
時(shí)飘千,說明程序已經(jīng)執(zhí)行完過dispatch_once
方法堂鲜,那么跳過dispatch_once
的block
的代碼,執(zhí)行block
之后的代碼护奈。
(3)當(dāng)onceToken != 0
且onceToken != -1
時(shí)缔莲,說明現(xiàn)在有線程(這里用線程A
表示)在執(zhí)行dispatch_once
方法,但是還沒執(zhí)行完畢霉旗。這個(gè)時(shí)候痴奏,當(dāng)前這條線程處于阻塞狀態(tài)
磺箕,等待線程A
執(zhí)行完畢。當(dāng)線程A
執(zhí)行完dispatch_once
方法時(shí)抛虫,onceToken
的值會(huì)變成-1
,這時(shí)候當(dāng)前這條線程繼續(xù)執(zhí)行松靡。
(4)單例
可以看成是一種特殊的實(shí)例,是一個(gè)全局的對(duì)象建椰,有且只有一個(gè)的對(duì)象雕欺。
十.快速迭代 dispatch_apply
NSLog(@"---");
dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
// 模擬耗時(shí)操作
[NSThread sleepForTimeInterval:(11 - index)];
NSLog(@"%zu",index);
});
NSLog(@"+++");
---
3
2
1
0
6
5
4
7
9
8
+++
dispatch_apply
是一個(gè)快速迭代的方法,類似于for循環(huán)
棉姐。
- 如果方法中傳入的是一個(gè)
串行隊(duì)列
屠列,那么dispatch_apply
里面的耗時(shí)操作就需要按順序同步執(zhí)行(相當(dāng)于異步串行隊(duì)列
,必須執(zhí)行完成一個(gè)任務(wù)之后伞矩,才能執(zhí)行下一個(gè)任務(wù)笛洛。不過一般不會(huì)這么做,這樣操作就失去了快速迭代
的意義)乃坤。 - 如果方法中傳入的是一個(gè)
全局隊(duì)列
苛让,那么里面多個(gè)耗時(shí)操作就可以同時(shí)進(jìn)行(相當(dāng)于異步并發(fā)隊(duì)列
,可以多個(gè)任務(wù)同時(shí)執(zhí)行)。 - 無論傳入的是
串行隊(duì)列
還是全局隊(duì)列
湿诊,dispatch_apply
方法都會(huì)阻塞當(dāng)前線程
等待所有任務(wù)執(zhí)行完畢狱杰,才能執(zhí)行dispatch_apply
方法后面的代碼(相當(dāng)于同步任務(wù)
)。
十一. 隊(duì)列組 dispatch_group
需求:在填寫個(gè)人資料頁(yè)面厅须,我們需要把個(gè)人的信息(名字仿畸,手機(jī)號(hào)等)上傳到后臺(tái),也需要把照片(身份證正反面拍照等)也上傳到后臺(tái)朗和,需要做兩個(gè)網(wǎng)絡(luò)請(qǐng)求错沽。當(dāng)兩個(gè)網(wǎng)絡(luò)請(qǐng)求都成功回調(diào)之后,返回上一個(gè)頁(yè)面眶拉。
1. 第一種方法:使用dispatch_group_notify
監(jiān)聽千埃。
NSLog(@"---");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:3];
NSLog(@"上傳個(gè)人信息");
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 3; ++i) {
[NSThread sleepForTimeInterval:1];
NSLog(@"上傳圖片資料");
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"返回上個(gè)界面");
});
NSLog(@"===");
---
===
上傳圖片資料
上傳圖片資料
上傳個(gè)人信息
上傳圖片資料
上傳個(gè)人信息
返回上個(gè)界面
-
dispatch_group_create()
創(chuàng)建一個(gè)隊(duì)列組。 -
dispatch_group_async
將任務(wù)放到隊(duì)列里面镀层,然后再講隊(duì)列放到隊(duì)列組里面镰禾。 -
dispatch_group_notify
監(jiān)聽隊(duì)列組中其他隊(duì)列的任務(wù)完成狀態(tài),當(dāng)所有的任務(wù)都執(zhí)行完成之后唱逢,將自身block
里面的任務(wù)也方法隊(duì)列組中吴侦,執(zhí)行任務(wù)。 -
dispatch_group_notify
不會(huì)阻塞當(dāng)前線程坞古。
2. 第二種方法:使用dispatch_group_wait
阻塞當(dāng)前線程备韧。
NSLog(@"---");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:3];
NSLog(@"上傳個(gè)人信息");
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 3; ++i) {
[NSThread sleepForTimeInterval:1];
NSLog(@"上傳圖片資料");
}
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"返回上個(gè)界面");
NSLog(@"===");
---
上傳圖片資料
上傳圖片資料
上傳個(gè)人信息
上傳圖片資料
上傳個(gè)人信息
返回上個(gè)界面
===
- 當(dāng)所有任務(wù)都完成之后,才執(zhí)行
dispatch_group_wait
后面的任務(wù)痪枫。 -
dispatch_group_wait
會(huì)阻塞當(dāng)前的線程织堂。
3. 第三種方法:使用dispatch_group_enter
和dispatch_group_leave
組合代替dispatch_group_async
叠艳。
NSLog(@"---");
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_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:3];
NSLog(@"上傳個(gè)人信息");
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (int i = 0; i < 3; ++i) {
[NSThread sleepForTimeInterval:1];
NSLog(@"上傳圖片資料");
}
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"返回上個(gè)界面");
});
NSLog(@"===");
---
===
上傳圖片資料
上傳圖片資料
上傳個(gè)人信息
上傳圖片資料
上傳個(gè)人信息
返回上個(gè)界面
-
dispatch_group_enter
表示把一個(gè)任務(wù)放到隊(duì)列組group
,并開始執(zhí)行易阳。 -
dispatch_group_leave
表示任務(wù)執(zhí)行完畢附较。 -
dispatch_group_enter
+dispatch_group_leave
=dispatch_group_async
- 可以用
dispatch_group_notify
,同樣也可以用dispatch_group_wait
(結(jié)果跟第2種
方法一樣)潦俺。區(qū)別是后者會(huì)照成當(dāng)前線程阻塞
拒课,前者不會(huì)。 - 當(dāng)
group
中的所有任務(wù)都執(zhí)行完畢時(shí)事示,才會(huì)執(zhí)行dispatch_group_wait
后面的任務(wù)早像,或者執(zhí)行追加到dispatch_group_notify
中的任務(wù)。
十二. 信號(hào)量 dispatch_semaphore
三個(gè)重要方法
-
dispatch_semaphore_create
:創(chuàng)建并初始化一個(gè)信號(hào)總量肖爵,一般為0
或者1
卢鹦。 -
dispatch_semaphore_signal
:發(fā)送一個(gè)信號(hào),即讓信號(hào)總量+1
劝堪。 -
dispatch_semaphore_wait
:如果當(dāng)前信號(hào)總量為0
冀自,那么阻塞當(dāng)前線程
,否則幅聘。信號(hào)總量-1
凡纳,正常執(zhí)行。
1. 異步線程變成同步帝蒿。
需求:有時(shí)候需要實(shí)時(shí)拿到異步里面耗時(shí)操作的結(jié)果,才能正確的執(zhí)行之后的代碼巷怜。
__block NSInteger i = 1;
// 創(chuàng)建一個(gè)信號(hào)總量為`0`的信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:2];
i ++;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"%ld",(long)i);
打印結(jié)果為:2
解釋:當(dāng)?shù)谝淮螆?zhí)行dispatch_semaphore_wait
時(shí)葛超,信號(hào)總量為0
,當(dāng)前線程阻塞
延塑。當(dāng)執(zhí)行完異步block
里面的耗時(shí)操作之后绣张,執(zhí)行了dispatch_semaphore_signal
,信號(hào)總量+1
关带。從block
里面出來第二次執(zhí)行dispatch_semaphore_wait
時(shí)侥涵,信號(hào)總量為1
,正常執(zhí)行宋雏。這樣就能等到異步執(zhí)行完之后芜飘,再執(zhí)行接下來的代碼(類似于同步執(zhí)行)。
2. 線程安全(線程鎖)
需求:有時(shí)候磨总,我們會(huì)在多個(gè)地方同時(shí)對(duì)同一個(gè)接口進(jìn)行調(diào)用嗦明,那如果每次調(diào)用過程會(huì)對(duì)下一次調(diào)用的結(jié)果有影響(有修改或者更變等操作),那么我們就必須保證該接口同一時(shí)間只能被一個(gè)地方調(diào)用蚪燕,這就是線程安全
娶牌。
- (void)viewDidLoad {
[super viewDidLoad];
self.semaphore = dispatch_semaphore_create(1);
}
- (void)tiaoyongjiekou{
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 模擬耗時(shí)操作
[NSThread sleepForTimeInterval:1.0];
dispatch_semaphore_signal(weakSelf.semaphore);
});
}
解釋:在外部創(chuàng)建一個(gè)信號(hào)總量為1
的信號(hào)量奔浅,當(dāng)?shù)谝淮握{(diào)用tiaoyongjiekou
方法,執(zhí)行到dispatch_semaphore_wait
時(shí)诗良,因?yàn)楫?dāng)前信號(hào)總量為1
汹桦,那么正常執(zhí)行并且信號(hào)總量-1
(此時(shí)信號(hào)總量為0
)。如果第一次調(diào)用還沒執(zhí)行完成鉴裹,第二次就開始調(diào)用舞骆,當(dāng)執(zhí)行到dispatch_semaphore_wait
時(shí),信號(hào)總量為0
壹罚,線程阻塞葛作,只能原地等待。等第一次調(diào)用結(jié)束猖凛,執(zhí)行完耗時(shí)操作之后赂蠢,執(zhí)行了dispatch_semaphore_signal
,信號(hào)總量+1
(此時(shí)信號(hào)總量為1
)辨泳,那么第二次調(diào)用才能繼續(xù)執(zhí)行虱岂。這樣就能確保同一時(shí)間只被調(diào)用一次,確保線安全菠红。