本文內(nèi)容
任務(wù)、隊(duì)列的概念匙赞、創(chuàng)建方式
任務(wù) + 隊(duì)列的6種組合的執(zhí)行方式
線程間如何通信
dispatch_once屡穗、dispatch_after、dispatch_apply(快速迭代)悼凑、dispatch_barrier(柵欄函數(shù))偿枕、dispatch_group(隊(duì)列組)、dispatch_semaphore(信號(hào)量)如何實(shí)現(xiàn)線程安全與線程同步
iOS多線程demo地址
上文說(shuō)到iOS 多線程- pThread和NSThread
這篇文章來(lái)講講GCD
GCD ??????????的優(yōu)點(diǎn)
- 可用于多核的并行運(yùn)算
- 會(huì)自動(dòng)利用更多的CPU內(nèi)核
- 自動(dòng)管理線程的生命周期(創(chuàng)建線程户辫、調(diào)度任務(wù)渐夸、銷毀線程)
- 只用關(guān)注執(zhí)行什么任務(wù),不用編寫任何線程管理代碼
1.任務(wù)
任務(wù):執(zhí)行的操作渔欢,放在block中的代碼
執(zhí)行任務(wù)的方式有兩種墓塌,主要區(qū)別是:是否等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束,是否具備開啟新線程的能力
奥额。
同步執(zhí)行(sync):同步添加當(dāng)前任務(wù)到指定的隊(duì)列中苫幢,在隊(duì)列中的任務(wù)全部結(jié)束之前,
會(huì)一直等待
垫挨,直到隊(duì)列中的任務(wù)全部完成后韩肝,才開始下一個(gè)任務(wù),只能在當(dāng)前線程中執(zhí)行任務(wù)九榔,不具備
開啟線程的能力哀峻。異步執(zhí)行 (async):異步添加當(dāng)前任務(wù)到指定隊(duì)列中涡相,
不會(huì)等待
隊(duì)列的任務(wù)執(zhí)行結(jié)束,直接開始執(zhí)行下一個(gè)任務(wù)剩蟀,可以在新的線程中執(zhí)行任務(wù)催蝗,具備
開啟線程的能力。
*注意:異步執(zhí)行 (async)雖然具有開啟線程的能力育特,但是不一定會(huì)開啟新的線程丙号,這跟任務(wù)所指定的隊(duì)列有關(guān)
2.隊(duì)列
隊(duì)列:存放任務(wù)的隊(duì)列,隊(duì)列是一種特殊的線性表缰冤,采用FIFO(先進(jìn)先出)的原則犬缨,即新任務(wù)總是被插入到隊(duì)列末尾,而讀取任務(wù)總是從隊(duì)列的頭部開始讀取锋谐,每讀取一個(gè)任務(wù)遍尺,隊(duì)列中則釋放一個(gè)任務(wù)。
隊(duì)列有兩種方式涮拗,都滿足FIFO原則乾戏,主要區(qū)別是:執(zhí)行順序不同,開啟線程數(shù)不同
- 串行隊(duì)列(Serial Dispatch Queue ):只開啟
一個(gè)
線程三热,一個(gè)
任務(wù)執(zhí)行完畢后鼓择,再執(zhí)行下一個(gè)任務(wù) - 并行隊(duì)列(Concurrent Dispatch Queue)?????????????? ??????????????????????????????????????????????????????????????:可以開啟
多個(gè)
線程,并且同時(shí)
執(zhí)行多個(gè)任務(wù)
3.使用步驟
- 創(chuàng)建一個(gè)隊(duì)列
- 將任務(wù)添加到隊(duì)列中就漾,系統(tǒng)根據(jù)任務(wù)執(zhí)行方式(同步呐能、異步)進(jìn)行執(zhí)行
3.1 隊(duì)列的創(chuàng)建、獲取
使用dispatch_queue_create
創(chuàng)建隊(duì)列
第一個(gè)參數(shù):隊(duì)列的唯一標(biāo)識(shí)符抑堡,用于DEBUG摆出,可以為空,推薦使用應(yīng)用程序ID這種逆序全局域名首妖。
第二個(gè)參數(shù):隊(duì)列類型偎漫,串行隊(duì)列DISPATCH_QUEUE_SERIAL
,并行隊(duì)列DISPATCH_QUEUE_CONCURRENT
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.queque", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建并行隊(duì)列
dispatch_queue_t queue2 = dispatch_queue_create("com.xiuxiu.queque", DISPATCH_QUEUE_CONCURRENT);
主隊(duì)列Main Dispatch Queue
: GCD提供一種特殊串行隊(duì)列:
- 所有放在主隊(duì)列中的任務(wù)有缆,都會(huì)放到主線程中執(zhí)行
- 可使用
dispatch_get_main_queue()
獲取主隊(duì)列
主隊(duì)列獲取方法
dispatch_queue_t queue = dispatch_get_main_queue()
全局并發(fā)隊(duì)列Global Dispatch Queue
: GCD默認(rèn)提供的全局并發(fā)隊(duì)列
并發(fā)隊(duì)列獲取方法:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_get_global_queue
獲取全局隊(duì)列
第一個(gè)參數(shù):隊(duì)列的優(yōu)先級(jí)
#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
第二個(gè)參數(shù):沒(méi)有使用象踊,用0 即可
3.2 任務(wù)的執(zhí)行方式
dispatch_async(queue, ^{
//異步執(zhí)行任務(wù)代碼
});
dispatch_sync(queue, ^{
//同步執(zhí)行任務(wù)代碼
});
第一個(gè)參數(shù)是隊(duì)列,那么隊(duì)列 + 任務(wù) 執(zhí)行方式就有6種組合(加上主隊(duì)列)
同步執(zhí)行 + 串行隊(duì)列
異步執(zhí)行 + 串行隊(duì)列
同步執(zhí)行 + 并行隊(duì)列
異步執(zhí)行 + 并行隊(duì)列
同步執(zhí)行 + 主隊(duì)列
異步執(zhí)行 + 主隊(duì)列
3.2.1.同步執(zhí)行 + 串行隊(duì)列
/*
同步執(zhí)行 + 串行隊(duì)列
不會(huì)開啟新線程棚壁,在當(dāng)前線程中執(zhí)行任務(wù)杯矩,一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)
*/
- (void)syncSerial{
NSLog(@" syncSerial start");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.syncSerial", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一袖外,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二史隆,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" syncSerial end");
}
:
前面的編號(hào)代表進(jìn)程
的編號(hào),一個(gè)APP的就是一個(gè)進(jìn)程曼验,進(jìn)程編號(hào)總是一致的泌射;
:
后面的編號(hào)代表線程
的編號(hào)头镊, 21898代表線程的編號(hào)
輸出結(jié)果:
- 在當(dāng)前線程中執(zhí)行任務(wù),沒(méi)有開啟新線程(同步執(zhí)行不具備開啟線程能力)魄幕,根據(jù)線程編號(hào)看出
- 所有任務(wù)都在
syncSerial start
和syncSerial end
之間執(zhí)行(同步執(zhí)行需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束) - 任務(wù)按順序執(zhí)行(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,一個(gè)任務(wù)執(zhí)行完畢后颖杏,再執(zhí)行下一個(gè)任務(wù))
2018-12-27 10:19:08.371664+0800 Thread[908:21898] syncSerial start
2018-12-27 10:19:08.371980+0800 Thread[908:21898] 任務(wù)一纯陨,i = 0
2018-12-27 10:19:09.372354+0800 Thread[908:21898] 任務(wù)一,i = 1
2018-12-27 10:19:10.372865+0800 Thread[908:21898] 任務(wù)一留储,i = 2
2018-12-27 10:19:11.373588+0800 Thread[908:21898] 任務(wù)二翼抠,i = 0
2018-12-27 10:19:12.374936+0800 Thread[908:21898] 任務(wù)二,i = 1
2018-12-27 10:19:13.375258+0800 Thread[908:21898] 任務(wù)二获讳,i = 2
2018-12-27 10:19:14.376006+0800 Thread[908:21898] syncSerial end
3.2.2.異步執(zhí)行 + 串行隊(duì)列
/*
異步執(zhí)行 + 串行隊(duì)列
會(huì)開啟新線程阴颖,但是因?yàn)殛?duì)列是串行的,一個(gè)任務(wù)執(zhí)行完畢后丐膝,再執(zhí)行下一個(gè)任務(wù)
*/
- (void)asyncSerial{
NSLog(@" asyncSerial start");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.asyncSerial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一量愧,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" asyncSerial end");
}
輸出結(jié)果:
- 開啟一條線程(異步執(zhí)行具備開啟線程的能力帅矗,因?yàn)槭谴嘘?duì)列偎肃,只開啟了一條線程)
- 所有任務(wù)都在
asyncSerial start
和asyncSerial end
之后執(zhí)行(異步執(zhí)行不會(huì)等待,繼續(xù)執(zhí)行任務(wù)) - 任務(wù)按順序執(zhí)行(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行浑此,任務(wù)一個(gè)接著一個(gè)執(zhí)行)
2018-12-27 10:43:17.947620+0800 Thread[1105:32316] asyncSerial start
2018-12-27 10:43:17.947916+0800 Thread[1105:32316] asyncSerial end
2018-12-27 10:43:17.948034+0800 Thread[1105:32360] 任務(wù)一累颂,i = 0
2018-12-27 10:43:18.953286+0800 Thread[1105:32360] 任務(wù)一,i = 1
2018-12-27 10:43:19.956284+0800 Thread[1105:32360] 任務(wù)一凛俱,i = 2
2018-12-27 10:43:20.961804+0800 Thread[1105:32360] 任務(wù)二紊馏,i = 0
2018-12-27 10:43:21.965620+0800 Thread[1105:32360] 任務(wù)二,i = 1
2018-12-27 10:43:22.967181+0800 Thread[1105:32360] 任務(wù)二蒲犬,i = 2
3.2.3. 同步執(zhí)行 + 并行隊(duì)列
/*
同步執(zhí)行 + 并行隊(duì)列
不會(huì)開啟新線程朱监,在當(dāng)前線程中執(zhí)行任務(wù),一個(gè)任務(wù)執(zhí)行完畢后暖哨,再執(zhí)行下一個(gè)任務(wù)
*/
- (void)syncConcurrent{
NSLog(@" syncConcurrent start");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.syncConcurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一赌朋,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" syncConcurrent end");
}
輸出結(jié)果:
- 在當(dāng)前線程中執(zhí)行任務(wù)篇裁,沒(méi)有開啟新線程(同步執(zhí)行不具備開啟線程能力)沛慢,根據(jù)線程編號(hào)看出,上面講過(guò)
- 所有任務(wù)都在
syncConcurrent start
和syncConcurrent end
之間執(zhí)行(同步執(zhí)行需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束) - 任務(wù)按順序執(zhí)行达布,雖然并行隊(duì)列可以開啟多個(gè)線程团甲,并且同時(shí)執(zhí)行多個(gè)任務(wù),但是因?yàn)橥綀?zhí)行不具備開啟線程的能力黍聂,只有當(dāng)前這一個(gè)線程躺苦,而且同步執(zhí)行需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束身腻,再執(zhí)行下一個(gè)任務(wù),所以任務(wù)只能一個(gè)接一個(gè)按照順序執(zhí)行匹厘。
2018-12-27 10:54:22.992819+0800 Thread[1210:37289] syncConcurrent start
2018-12-27 10:54:22.993023+0800 Thread[1210:37289] 任務(wù)一嘀趟,i = 0
2018-12-27 10:54:23.994423+0800 Thread[1210:37289] 任務(wù)一,i = 1
2018-12-27 10:54:24.995012+0800 Thread[1210:37289] 任務(wù)一愈诚,i = 2
2018-12-27 10:54:25.996057+0800 Thread[1210:37289] 任務(wù)二她按,i = 0
2018-12-27 10:54:26.997429+0800 Thread[1210:37289] 任務(wù)二,i = 1
2018-12-27 10:54:27.998885+0800 Thread[1210:37289] 任務(wù)二炕柔,i = 2
2018-12-27 10:54:29.000327+0800 Thread[1210:37289] syncConcurrent end
3.2.4.異步執(zhí)行 + 并發(fā)隊(duì)列
/*
異步執(zhí)行 + 并行隊(duì)列
開啟多個(gè)線程酌泰,任務(wù)交替執(zhí)行
*/
- (void)asyncConcurrent{
NSLog(@" asyncConcurrent start");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二匕累,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" asyncConcurrent end");
}
輸出結(jié)果:
- 開啟了2個(gè)新線程(異步執(zhí)行具備開啟線程的能力)
- 所有任務(wù)都在
asyncConcurrent start
和asyncConcurrent end
之后執(zhí)行(異步執(zhí)行不會(huì)等待陵刹,繼續(xù)執(zhí)行任務(wù)) - 任務(wù)交替執(zhí)行(異步執(zhí)行具備開啟線程的能力,并且并行隊(duì)列可以開啟多個(gè)線程欢嘿,同時(shí)執(zhí)行多個(gè)任務(wù))
2018-12-27 11:18:23.174090+0800 Thread[1210:37289] asyncConcurrent start
2018-12-27 11:18:23.174253+0800 Thread[1210:37289] asyncConcurrent end
2018-12-27 11:18:23.174351+0800 Thread[1210:47038] 任務(wù)二衰琐,i = 0
2018-12-27 11:18:23.174401+0800 Thread[1210:37362] 任務(wù)一,i = 0
2018-12-27 11:18:24.177759+0800 Thread[1210:37362] 任務(wù)一际插,i = 1
2018-12-27 11:18:24.177759+0800 Thread[1210:47038] 任務(wù)二碘耳,i = 1
2018-12-27 11:18:25.178650+0800 Thread[1210:47038] 任務(wù)二,i = 2
2018-12-27 11:18:25.178650+0800 Thread[1210:37362] 任務(wù)一框弛,i = 2
3.2.5.同步執(zhí)行 + 主隊(duì)列
同步執(zhí)行 + 主隊(duì)列 在不同線程中調(diào)用辛辨,結(jié)果不一樣。
在主線程中調(diào)用瑟枫,出現(xiàn)死鎖
在其他線程中調(diào)用斗搞,不會(huì)開啟線程,一個(gè)任務(wù)執(zhí)行完畢后慷妙,再執(zhí)行下一個(gè)任務(wù)
3.2.5.1在主線程調(diào)用
/*
同步執(zhí)行 + 主隊(duì)列
在主線程中調(diào)用僻焚,出現(xiàn)死鎖
*/
- (void)syncMain{
NSLog(@" syncMain start");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二膝擂,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" syncMain end");
}
輸出結(jié)果:
在Xcode10 上運(yùn)行崩潰虑啤,只輸出了syncMain start
為什么?
因?yàn)槲覀冊(cè)谥骶€程中執(zhí)行 syncMain
方法架馋,相當(dāng)于把 syncMain
任務(wù)放到主線程的隊(duì)列中狞山,而同步執(zhí)行會(huì)等待當(dāng)前隊(duì)列中的任務(wù)執(zhí)行完畢后,才會(huì)接著執(zhí)行叉寂。我們把任務(wù)一追加到主隊(duì)列中萍启,任務(wù)一會(huì)等待主線程處理完syncMain
方法,而syncMain
方法又需要等待任務(wù)一執(zhí)行完畢,才能繼續(xù)執(zhí)行勘纯,雙方都在等待局服,所以線程死鎖,任務(wù)無(wú)法執(zhí)行驳遵。
2018-12-27 11:26:17.011738+0800 Thread[1443:50531] syncMain start
3.2.5.2 在其他線程調(diào)用
/*
同步執(zhí)行 + 主隊(duì)列
在其他線程中調(diào)用淫奔,不會(huì)開啟線程,一個(gè)任務(wù)執(zhí)行完畢后堤结,再執(zhí)行下一個(gè)任務(wù)
*/
- (void)syncMain{
NSLog(@"主線程");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@" syncMain start");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一搏讶,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" syncMain end");
});
}
輸出結(jié)果:
- 沒(méi)有開啟線程霍殴,放在主隊(duì)列的任務(wù)都在主線程中執(zhí)行
- 所有任務(wù)都在
syncMain start
和syncMain end
之間執(zhí)行(同步執(zhí)行需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束) - 任務(wù)按順序執(zhí)行(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接著一個(gè)執(zhí)行)
2018-12-27 14:22:03.964047+0800 Thread[2134:84333] 主線程
2018-12-27 14:22:03.964238+0800 Thread[2134:84385] syncMain start
2018-12-27 14:22:03.965009+0800 Thread[2134:84333] 任務(wù)一系吩,i = 0
2018-12-27 14:22:04.966453+0800 Thread[2134:84333] 任務(wù)一来庭,i = 1
2018-12-27 14:22:05.967903+0800 Thread[2134:84333] 任務(wù)一,i = 2
2018-12-27 14:22:06.968817+0800 Thread[2134:84333] 任務(wù)二穿挨,i = 0
2018-12-27 14:22:07.969877+0800 Thread[2134:84333] 任務(wù)二月弛,i = 1
2018-12-27 14:22:08.970376+0800 Thread[2134:84333] 任務(wù)二,i = 2
2018-12-27 14:22:09.971810+0800 Thread[2134:84385] syncMain end
3.2.6.異步執(zhí)行 + 主隊(duì)列
/*
異步執(zhí)行 + 主隊(duì)列
不會(huì)開啟新線程科盛,在主線程中執(zhí)行帽衙,一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)
*/
- (void)asyncMain{
NSLog(@" asyncMain start");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一贞绵,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二厉萝,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" asyncMain end");
}
輸出結(jié)果:
- 沒(méi)有開啟線程,放在主隊(duì)列的任務(wù)都在主線程中執(zhí)行
- 所有任務(wù)都在
asyncMain start
和asyncMain end
之后執(zhí)行(異步執(zhí)行不會(huì)等待榨崩,繼續(xù)執(zhí)行任務(wù)) - 任務(wù)按順序執(zhí)行(因?yàn)橹麝?duì)列是串行隊(duì)列谴垫,每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接著一個(gè)執(zhí)行)
2018-12-27 14:33:15.293261+0800 Thread[2134:84333] asyncMain start
2018-12-27 14:33:15.293406+0800 Thread[2134:84333] asyncMain end
2018-12-27 14:33:15.293646+0800 Thread[2134:84333] 任務(wù)一母蛛,i = 0
2018-12-27 14:33:16.293875+0800 Thread[2134:84333] 任務(wù)一翩剪,i = 1
2018-12-27 14:33:17.295251+0800 Thread[2134:84333] 任務(wù)一,i = 2
2018-12-27 14:33:18.296751+0800 Thread[2134:84333] 任務(wù)二彩郊,i = 0
2018-12-27 14:33:19.297602+0800 Thread[2134:84333] 任務(wù)二前弯,i = 1
2018-12-27 14:33:20.298579+0800 Thread[2134:84333] 任務(wù)二,i = 2
4.線程間通信
在iOS開發(fā)工程中秫逝,我們一般在主線程中進(jìn)行UI刷新恕出,如:點(diǎn)擊、拖拽筷登、滾動(dòng)事件剃根,耗時(shí)操作放在其他線程中,而當(dāng)耗時(shí)操作結(jié)束后前方,回到主線程狈醉,就需要用到線程間的通信廉油。
在全局隊(duì)列中執(zhí)行任務(wù),任務(wù)完成后苗傅,切回主線程
- (void)gcdCommunication{
NSLog(@"我在主線程");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一抒线,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主線程");
});
});
}
輸出結(jié)果:
2018-12-27 15:05:55.188427+0800 Thread[2519:102865] 我在主線程
2018-12-27 15:05:55.188635+0800 Thread[2519:102899] 任務(wù)一,i = 0
2018-12-27 15:05:56.189689+0800 Thread[2519:102899] 任務(wù)一渣慕,i = 1
2018-12-27 15:05:57.191253+0800 Thread[2519:102899] 任務(wù)一嘶炭,i = 2
2018-12-27 15:05:58.195350+0800 Thread[2519:102865] 回到主線程
5.dispatch_once
創(chuàng)建單例或者整個(gè)程序只運(yùn)行一次的代碼,可以使用dispatch_once
逊桦,dispatch_once
函數(shù)保證這個(gè)程序運(yùn)行過(guò)程中只被執(zhí)行一次眨猎,即時(shí)在多線程情況下也是安全的。
/*
驗(yàn)證dispatch_once
*/
- (void)testGcdOnce{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (NSInteger i = 0; i < 3; i ++) {
[self gcdOnce];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (NSInteger i = 0; i < 3; i ++) {
[self gcdOnce];
}
});
}
- (void)gcdOnce{
NSLog(@"%s",__func__);
static TicketManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[TicketManager alloc]init];
NSLog(@"創(chuàng)建對(duì)象");
});
}
輸出結(jié)果:dispatch_once
里面只被執(zhí)行了一次
2018-12-27 15:19:31.228794+0800 Thread[2702:110251] -[ViewController gcdOnce]
2018-12-27 15:19:31.228794+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229290+0800 Thread[2702:110251] 創(chuàng)建對(duì)象
2018-12-27 15:19:31.229516+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229514+0800 Thread[2702:110251] -[ViewController gcdOnce]
2018-12-27 15:19:31.229630+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229678+0800 Thread[2702:110251] -[ViewController gcdOnce]
6.dispatch_after
dispatch_after
并不是在指定時(shí)間之后才執(zhí)行處理强经,而是在指定時(shí)間之后將任務(wù)追加到隊(duì)列中睡陪,這個(gè)指定時(shí)間并不是絕對(duì)準(zhǔn)確的,想要大致完成延時(shí)任務(wù)可以使用dispatch_after
函數(shù)實(shí)現(xiàn)
- (void)gcdAfter{
NSLog(@"%s",__func__);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%s",__func__);
});;
}
輸出結(jié)果:大致為1秒
2018-12-27 15:28:23.416958+0800 Thread[2781:114534] -[ViewController gcdAfter]
2018-12-27 15:28:24.513064+0800 Thread[2781:114534] -[ViewController gcdAfter]_block_invoke
7. dispatch_apply(快速迭代)
dispatch_apply
快速迭代方法匿情,按照指定次數(shù)將指定的任務(wù)添加到隊(duì)列中兰迫,并等待隊(duì)列中的任務(wù)全部執(zhí)行結(jié)束。
如果在串行隊(duì)列中使用dispatch_apply
函數(shù)炬称,就和for
循環(huán)遍歷一樣汁果,按照順序執(zhí)行,體現(xiàn)不出快速迭代的意義玲躯。
如果在并行隊(duì)列中使用dispatch_apply
函數(shù)据德,dispatch_apply
可以在多個(gè)線程中同時(shí)遍歷多個(gè)數(shù)字。
7.1 在串行隊(duì)列使用dispatch_apply
將在主隊(duì)列dispatch_get_main_queue
的遍歷任務(wù)放在并行隊(duì)列dispatch_get_global_queue
中跷车,為了避免上面講的死鎖問(wèn)題,關(guān)注apply begin
和apply end
之間的代碼即可
/*
dispatch_apply:快速迭代
*/
- (void)gcdApply{
NSLog(@"主線程");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"apply begin");
dispatch_apply(6, dispatch_get_main_queue(), ^(size_t index) {
NSLog(@"index = %zu",index);
});
NSLog(@"apply end");
});
}
輸出結(jié)果:
- 沒(méi)有開啟線程(主隊(duì)列的任務(wù)只在主線程中執(zhí)行)
-
apply end
在最后輸出(dispatch_apply
函數(shù)會(huì)等待隊(duì)列全部任務(wù)執(zhí)行結(jié)束)
2018-12-27 15:58:28.788666+0800 Thread[3090:128936] 主線程
2018-12-27 15:58:28.788880+0800 Thread[3090:128988] apply begin
2018-12-27 15:58:28.819630+0800 Thread[3090:128936] index = 0
2018-12-27 15:58:28.819791+0800 Thread[3090:128936] index = 1
2018-12-27 15:58:28.819897+0800 Thread[3090:128936] index = 2
2018-12-27 15:58:28.820107+0800 Thread[3090:128936] index = 3
2018-12-27 15:58:28.820396+0800 Thread[3090:128936] index = 4
2018-12-27 15:58:28.820503+0800 Thread[3090:128936] index = 5
2018-12-27 15:58:28.820752+0800 Thread[3090:128988] apply end
7.2在并行隊(duì)列使用dispatch_apply
/*
dispatch_apply:快速迭代
*/
- (void)gcdApply{
NSLog(@"apply begin");
dispatch_apply(6, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"index = %zu",index);
});
NSLog(@"apply end");
}
輸出結(jié)果:
- 開啟線程(看線程編號(hào)得出)
-
apply end
在最后輸出(dispatch_apply
函數(shù)會(huì)等待隊(duì)列全部任務(wù)執(zhí)行結(jié)束)
2018-12-27 15:39:06.219402+0800 Thread[2918:120358] apply begin
2018-12-27 15:39:06.219636+0800 Thread[2918:120391] index = 1
2018-12-27 15:39:06.219619+0800 Thread[2918:120358] index = 0
2018-12-27 15:39:06.219746+0800 Thread[2918:120391] index = 3
2018-12-27 15:39:06.219747+0800 Thread[2918:120392] index = 4
2018-12-27 15:39:06.219737+0800 Thread[2918:120358] index = 2
2018-12-27 15:39:06.219832+0800 Thread[2918:120391] index = 5
2018-12-27 15:39:06.219933+0800 Thread[2918:120358] apply end
需求1:我們需要異步執(zhí)行兩個(gè)操作(一個(gè)操作可以是一個(gè)任務(wù)晋控,也可以是多個(gè)任務(wù),這里是兩個(gè)任務(wù))姓赤,而且第一組操作結(jié)束后赡译,才能開始第二組操作,如何實(shí)現(xiàn)呢不铆?
這里的意思其實(shí)保持線程同步蝌焚,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)的意思。
方法1:使用柵欄函數(shù)
方法2:使用dispatch_group
方式3 :使用dispatch_semaphore
這三個(gè)方法都會(huì)在下面一一講解的誓斥。
總結(jié):就當(dāng)前這個(gè)需求只洒,使用柵欄函數(shù)會(huì)比較簡(jiǎn)單,所有方法講完具體實(shí)現(xiàn)就可以看出來(lái)了不用創(chuàng)建group
或者semaphore
,直接放個(gè)柵欄在中間劳坑,分隔兩個(gè)操作毕谴。
8.dispatch_barrier(柵欄函數(shù))
柵欄函數(shù)存在的意義:先執(zhí)行柵欄函數(shù)之前的任務(wù),再執(zhí)行柵欄函數(shù)中的任務(wù),最后執(zhí)行柵欄函數(shù)之后的任務(wù)涝开,增加?xùn)艡诤瘮?shù)不影響原有隊(duì)列的任務(wù)執(zhí)行方式循帐,也就是柵欄函數(shù)之前隊(duì)列的任務(wù)是什么執(zhí)行方式,柵欄函數(shù)之后隊(duì)列的任務(wù)還是什么執(zhí)行方式舀武。
柵欄函數(shù)分為:dispatch_barrier_async
和dispatch_barrier_sync
區(qū)別就是 :添加?xùn)艡诤瘮?shù)后面任務(wù)到隊(duì)列的時(shí)間不一樣拄养。
dispatch_barrier_sync
:需要
等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束后,才會(huì)添加?xùn)艡诤瘮?shù)后的任務(wù)到隊(duì)列中银舱。
dispatch_barrier_async
: 不需要
等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束瘪匿,就已經(jīng)將柵欄函數(shù)后的任務(wù)到隊(duì)列中。
8.1 dispatch_barrier_sync
實(shí)現(xiàn)代碼
- (void)gcdBarrier{
NSLog(@"主線程");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一寻馏,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二棋弥,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
//1.dispatch_barrier_sync
dispatch_barrier_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" barrier1,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" barrier2诚欠,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@"---------barrier代碼后面----------------");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)三嘁锯,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
輸出結(jié)果:
-
先
執(zhí)行任務(wù)一聂薪、二,再
執(zhí)行柵欄函數(shù)中的barrier1
蝗羊、barrier2
藏澳,最后
執(zhí)行任務(wù)三、四耀找; - 任務(wù)一翔悠、二交替執(zhí)行(異步執(zhí)行),任務(wù)三野芒、四交替執(zhí)行(異步執(zhí)行)蓄愁,印證柵欄函數(shù)不影響隊(duì)列任務(wù)的執(zhí)行方式;
- 橫線在柵欄函數(shù)之后輸出(
dispatch_barrier_sync
:需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束后狞悲,才會(huì)添加?xùn)艡诤瘮?shù)后的任務(wù)到隊(duì)列中)撮抓。
2018-12-28 17:07:54.227141+0800 Thread[11766:169507] 主線程
2018-12-28 17:07:54.227455+0800 Thread[11766:169563] 任務(wù)二,i = 0
2018-12-28 17:07:54.227482+0800 Thread[11766:169565] 任務(wù)一摇锋,i = 0
2018-12-28 17:07:55.231426+0800 Thread[11766:169563] 任務(wù)二丹拯,i = 1
2018-12-28 17:07:55.231426+0800 Thread[11766:169565] 任務(wù)一,i = 1
2018-12-28 17:07:56.232952+0800 Thread[11766:169563] 任務(wù)二荸恕,i = 2
2018-12-28 17:07:56.232985+0800 Thread[11766:169565] 任務(wù)一乖酬,i = 2
2018-12-28 17:07:57.236980+0800 Thread[11766:169507] barrier1,i = 0
2018-12-28 17:07:58.238423+0800 Thread[11766:169507] barrier1融求,i = 1
2018-12-28 17:07:59.239776+0800 Thread[11766:169507] barrier1咬像,i = 2
2018-12-28 17:08:00.240315+0800 Thread[11766:169507] barrier2,i = 0
2018-12-28 17:08:01.240863+0800 Thread[11766:169507] barrier2,i = 1
2018-12-28 17:08:02.242310+0800 Thread[11766:169507] barrier2县昂,i = 2
2018-12-28 17:08:03.242826+0800 Thread[11766:169507] ---------barrier代碼后面----------------
2018-12-28 17:08:03.243200+0800 Thread[11766:169564] 任務(wù)三肮柜,i = 0
2018-12-28 17:08:03.243315+0800 Thread[11766:169663] 任務(wù)四,i = 0
2018-12-28 17:08:04.247765+0800 Thread[11766:169663] 任務(wù)四七芭,i = 1
2018-12-28 17:08:04.247765+0800 Thread[11766:169564] 任務(wù)三素挽,i = 1
2018-12-28 17:08:05.252405+0800 Thread[11766:169663] 任務(wù)四,i = 2
2018-12-28 17:08:05.252405+0800 Thread[11766:169564] 任務(wù)三狸驳,i = 2
8.2dispatch_barrier_async
實(shí)現(xiàn)代碼
- (void)gcdBarrier{
NSLog(@"主線程");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一预明,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
//2.dispatch_barrier_async
dispatch_barrier_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" barrier1耙箍,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" barrier2撰糠,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@"---------barrier代碼后面----------------");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)四辩昆,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
輸出結(jié)果:
-
先
執(zhí)行任務(wù)一阅酪、二,再
執(zhí)行柵欄函數(shù)中的barrier1
汁针、barrier2
术辐,最后
執(zhí)行任務(wù)三、四施无; - 任務(wù)一辉词、二交替執(zhí)行(異步執(zhí)行),任務(wù)三猾骡、四交替執(zhí)行(異步執(zhí)行)瑞躺,印證柵欄函數(shù)不影響隊(duì)列任務(wù)的執(zhí)行方式;
- 橫線在柵欄函數(shù)之前輸出(
dispatch_barrier_async
:不需要
等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束兴想,就已經(jīng)將柵欄函數(shù)后的任務(wù)到隊(duì)列中)幢哨。
2018-12-28 17:10:58.908322+0800 Thread[11798:171154] 主線程
2018-12-28 17:10:58.908586+0800 Thread[11798:171209] 任務(wù)一,i = 0
2018-12-28 17:10:58.908586+0800 Thread[11798:171154] ---------barrier代碼后面----------------
2018-12-28 17:10:58.908616+0800 Thread[11798:171207] 任務(wù)二嫂便,i = 0
2018-12-28 17:10:59.912811+0800 Thread[11798:171207] 任務(wù)二捞镰,i = 1
2018-12-28 17:10:59.912811+0800 Thread[11798:171209] 任務(wù)一,i = 1
2018-12-28 17:11:00.917006+0800 Thread[11798:171207] 任務(wù)二毙替,i = 2
2018-12-28 17:11:00.917047+0800 Thread[11798:171209] 任務(wù)一曼振,i = 2
2018-12-28 17:11:01.919238+0800 Thread[11798:171209] barrier1,i = 0
2018-12-28 17:11:02.921574+0800 Thread[11798:171209] barrier1蔚龙,i = 1
2018-12-28 17:11:03.924472+0800 Thread[11798:171209] barrier1冰评,i = 2
2018-12-28 17:11:04.929280+0800 Thread[11798:171209] barrier2,i = 0
2018-12-28 17:11:05.930060+0800 Thread[11798:171209] barrier2木羹,i = 1
2018-12-28 17:11:06.931831+0800 Thread[11798:171209] barrier2甲雅,i = 2
2018-12-28 17:11:07.934022+0800 Thread[11798:171209] 任務(wù)三解孙,i = 0
2018-12-28 17:11:07.934022+0800 Thread[11798:171207] 任務(wù)四,i = 0
2018-12-28 17:11:08.939572+0800 Thread[11798:171207] 任務(wù)四抛人,i = 1
2018-12-28 17:11:08.939573+0800 Thread[11798:171209] 任務(wù)三弛姜,i = 1
2018-12-28 17:11:09.942792+0800 Thread[11798:171209] 任務(wù)三,i = 2
2018-12-28 17:11:09.942829+0800 Thread[11798:171207] 任務(wù)四妖枚,i = 2
9.dispatch_group(隊(duì)列組)
任務(wù)的執(zhí)行是先創(chuàng)建一個(gè)任務(wù)廷臼,放入隊(duì)列進(jìn)行執(zhí)行。而隊(duì)列組就是用來(lái)存放隊(duì)列的一個(gè)組绝页。
將隊(duì)列放入隊(duì)列組中可以使用 dispatch_group_async
或者dispatch_group_enter
和dispatch_group_leave
的組合實(shí)現(xiàn)荠商。
隊(duì)列組的任務(wù)結(jié)束完成后,調(diào)用dispatch_group_notify
可以回到指定
線程執(zhí)行任務(wù)续誉,調(diào)用dispatch_group_wait
可以回到當(dāng)前
線程執(zhí)行任務(wù)(阻塞
當(dāng)前線程)莱没。
就上面的需求1,用dispatch_group
方式實(shí)現(xiàn)
1. 我們用dispatch_group_async
+ dispatch_group_notify
來(lái)進(jìn)行實(shí)現(xiàn)
- (void)requirementGroupAsync{
NSLog(@"主線程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一酷鸦,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二饰躲,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_group_notify(group, queue, ^{
NSLog(@"------dispatch_group_notify------");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)四臼隔,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
});
}
輸出結(jié)果:先執(zhí)行任務(wù)一嘹裂、二,再執(zhí)行任務(wù)三摔握、四寄狼;
2018-12-28 17:59:23.695495+0800 Thread[12227:189820] 主線程
2018-12-28 17:59:23.695775+0800 Thread[12227:189867] 任務(wù)一,i = 0
2018-12-28 17:59:23.695778+0800 Thread[12227:189868] 任務(wù)二盒发,i = 0
2018-12-28 17:59:24.697513+0800 Thread[12227:189867] 任務(wù)一,i = 1
2018-12-28 17:59:24.697514+0800 Thread[12227:189868] 任務(wù)二狡逢,i = 1
2018-12-28 17:59:25.701243+0800 Thread[12227:189868] 任務(wù)二宁舰,i = 2
2018-12-28 17:59:25.701243+0800 Thread[12227:189867] 任務(wù)一,i = 2
2018-12-28 17:59:26.705407+0800 Thread[12227:189868] ------dispatch_group_notify------
2018-12-28 17:59:26.705799+0800 Thread[12227:189868] 任務(wù)三奢浑,i = 0
2018-12-28 17:59:26.705803+0800 Thread[12227:189866] 任務(wù)四蛮艰,i = 0
2018-12-28 17:59:27.709876+0800 Thread[12227:189868] 任務(wù)三,i = 1
2018-12-28 17:59:27.709873+0800 Thread[12227:189866] 任務(wù)四雀彼,i = 1
2018-12-28 17:59:28.713711+0800 Thread[12227:189866] 任務(wù)四壤蚜,i = 2
2018-12-28 17:59:28.713711+0800 Thread[12227:189868] 任務(wù)三,i = 2
2. 我們用dispatch_group_async
+ dispatch_group_wait
來(lái)進(jìn)行實(shí)現(xiàn)
- (void)requirementGroupAsync{
NSLog(@"主線程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一徊哑,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二袜刷,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
//2.dispatch_group_wait
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"-----dispatch_group_wait----");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)四莺丑,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
輸出結(jié)果:
- 先執(zhí)行任務(wù)一著蟹、二墩蔓,再執(zhí)行任務(wù)三、四萧豆;
-
dispatch_group_wait
在主線程輸出奸披,就如之前所說(shuō)dispatch_group_wait
會(huì)阻塞當(dāng)前線程。
2018-12-28 18:07:54.379565+0800 Thread[12300:193286] 主線程
2018-12-28 18:07:54.379782+0800 Thread[12300:193334] 任務(wù)二涮雷,i = 0
2018-12-28 18:07:54.379783+0800 Thread[12300:193332] 任務(wù)一阵面,i = 0
2018-12-28 18:07:55.384688+0800 Thread[12300:193334] 任務(wù)二,i = 1
2018-12-28 18:07:55.384720+0800 Thread[12300:193332] 任務(wù)一洪鸭,i = 1
2018-12-28 18:07:56.385807+0800 Thread[12300:193332] 任務(wù)一样刷,i = 2
2018-12-28 18:07:56.385808+0800 Thread[12300:193334] 任務(wù)二,i = 2
2018-12-28 18:07:57.386847+0800 Thread[12300:193286] -----dispatch_group_wait----
2018-12-28 18:07:57.387161+0800 Thread[12300:193334] 任務(wù)三卿嘲,i = 0
2018-12-28 18:07:57.387174+0800 Thread[12300:193332] 任務(wù)四颂斜,i = 0
2018-12-28 18:07:58.387806+0800 Thread[12300:193332] 任務(wù)四,i = 1
2018-12-28 18:07:58.387808+0800 Thread[12300:193334] 任務(wù)三拾枣,i = 1
2018-12-28 18:07:59.390173+0800 Thread[12300:193332] 任務(wù)四沃疮,i = 2
2018-12-28 18:07:59.390209+0800 Thread[12300:193334] 任務(wù)三,i = 2
3. 我們用dispatch_group_enter
+ dispatch_group_leave
+ dispatch_group_notify
來(lái)進(jìn)行實(shí)現(xiàn)
- (void)requirementGroupEnter{
NSLog(@"主線程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一梅肤,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二司蔬,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_group_leave(group);
});
//1.dispatch_group_notify
dispatch_group_notify(group, queue, ^{
NSLog(@"------dispatch_group_notify------");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)三左医,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
});
}
輸出結(jié)果:先執(zhí)行任務(wù)一芥映、二奈偏,再執(zhí)行任務(wù)三唁盏、四;
2018-12-28 18:19:52.294127+0800 Thread[12422:198467] 主線程
2018-12-28 18:19:52.294429+0800 Thread[12422:198501] 任務(wù)一刽严,i = 0
2018-12-28 18:19:52.294438+0800 Thread[12422:198504] 任務(wù)二撑螺,i = 0
2018-12-28 18:19:53.297632+0800 Thread[12422:198504] 任務(wù)二主胧,i = 1
2018-12-28 18:19:53.297642+0800 Thread[12422:198501] 任務(wù)一,i = 1
2018-12-28 18:19:54.302891+0800 Thread[12422:198501] 任務(wù)一诀黍,i = 2
2018-12-28 18:19:54.302891+0800 Thread[12422:198504] 任務(wù)二歇拆,i = 2
2018-12-28 18:19:55.307933+0800 Thread[12422:198501] ------dispatch_group_notify------
2018-12-28 18:19:55.308242+0800 Thread[12422:198501] 任務(wù)三渠啊,i = 0
2018-12-28 18:19:55.308258+0800 Thread[12422:198502] 任務(wù)四,i = 0
2018-12-28 18:19:56.312331+0800 Thread[12422:198501] 任務(wù)三权旷,i = 1
2018-12-28 18:19:56.312331+0800 Thread[12422:198502] 任務(wù)四替蛉,i = 1
2018-12-28 18:19:57.313584+0800 Thread[12422:198502] 任務(wù)四,i = 2
2018-12-28 18:19:57.313584+0800 Thread[12422:198501] 任務(wù)三拄氯,i = 2
4. 我們用dispatch_group_enter
+ dispatch_group_leave
+ dispatch_group_wait
來(lái)進(jìn)行實(shí)現(xiàn)
- (void)requirementGroupEnter{
NSLog(@"主線程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一躲查,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_group_leave(group);
});
// 2.dispatch_group_wait
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"-----dispatch_group_wait----");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)三译柏,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)四镣煮,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
- 先執(zhí)行任務(wù)一、二鄙麦,再執(zhí)行任務(wù)三典唇、四;
-
dispatch_group_wait
在主線程輸出黔衡,就如之前所說(shuō)dispatch_group_wait
會(huì)阻塞當(dāng)前線程
2018-12-28 18:21:29.197243+0800 Thread[12446:199547] 主線程
2018-12-28 18:21:29.197534+0800 Thread[12446:199590] 任務(wù)一蚓聘,i = 0
2018-12-28 18:21:29.197598+0800 Thread[12446:199591] 任務(wù)二,i = 0
2018-12-28 18:21:30.200867+0800 Thread[12446:199591] 任務(wù)二盟劫,i = 1
2018-12-28 18:21:30.200870+0800 Thread[12446:199590] 任務(wù)一夜牡,i = 1
2018-12-28 18:21:31.204217+0800 Thread[12446:199590] 任務(wù)一,i = 2
2018-12-28 18:21:31.204215+0800 Thread[12446:199591] 任務(wù)二侣签,i = 2
2018-12-28 18:21:32.205551+0800 Thread[12446:199547] -----dispatch_group_wait----
2018-12-28 18:21:32.205926+0800 Thread[12446:199590] 任務(wù)三塘装,i = 0
2018-12-28 18:21:32.205930+0800 Thread[12446:199591] 任務(wù)四,i = 0
2018-12-28 18:21:33.210243+0800 Thread[12446:199590] 任務(wù)三影所,i = 1
2018-12-28 18:21:33.210413+0800 Thread[12446:199591] 任務(wù)四蹦肴,i = 1
2018-12-28 18:21:34.215728+0800 Thread[12446:199590] 任務(wù)三,i = 2
2018-12-28 18:21:34.215728+0800 Thread[12446:199591] 任務(wù)四猴娩,i = 2
那dispatch_group_async
和dispatch_group_enter
+ dispatch_group_leave
有什么區(qū)別呢阴幌?
需求2:異步執(zhí)行兩個(gè)網(wǎng)絡(luò)請(qǐng)求,兩個(gè)網(wǎng)絡(luò)請(qǐng)求執(zhí)行結(jié)束后卷中,進(jìn)行一定的操作矛双。
這里sendRequest
用來(lái)代表網(wǎng)絡(luò)請(qǐng)求
- (void)sendRequest:(void (^)(void))block{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 1");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 1");
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block();
}
});
});
}
- (void)sendRequest2:(void (^)(void))block{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 2");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 2");
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block();
}
});
});
}
1. 用dispatch_group_async
實(shí)現(xiàn)
- (void)gcdGroupAsync{
NSLog(@"主線程");
// dispatch_group_async 里面,應(yīng)該放同步代碼蟆豫,而不是異步代碼
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[self sendRequest:^{
NSLog(@"sendRequest done");
}];
});
dispatch_group_async(group, queue, ^{
[self sendRequest2:^{
NSLog(@"sendRequest2 done");
}];
});
dispatch_group_notify(group, queue, ^{
NSLog(@"all task over");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主線程刷新UI");
});
});
}
輸出結(jié)果:顯示不符合需求议忽,task1和task2還沒(méi)有結(jié)束,就輸出all task over 咯十减,為啥子呢栈幸?
原因:因?yàn)?code>dispatch_group_async里面放入的是異步的任務(wù)愤估,dispatch_group_async
執(zhí)行了sendRequest
這行代碼后,就認(rèn)為sendRequest
已經(jīng)執(zhí)行完畢了(其實(shí)還沒(méi)有回調(diào)回來(lái))速址,group
不再持有這個(gè)任務(wù)玩焰,就會(huì)執(zhí)行下面的dispatch_group_async
,而sendRequest2
同理芍锚,group
沒(méi)有任務(wù)時(shí)震捣,就會(huì)執(zhí)行dispatch_group_notify
里面的任務(wù),所以造成這樣子的輸出結(jié)果闹炉。
由此可見(jiàn):dispatch_group_async
里面適合放入同步代碼蒿赢,而不是異步代碼。
2018-12-29 17:13:42.710421+0800 Thread[17395:417245] 主線程
2018-12-29 17:13:42.710700+0800 Thread[17395:417334] start task 2
2018-12-29 17:13:42.710726+0800 Thread[17395:417333] all task over
2018-12-29 17:13:42.710708+0800 Thread[17395:417331] start task 1
2018-12-29 17:13:42.733662+0800 Thread[17395:417245] 回到主線程刷新UI
2018-12-29 17:13:45.714431+0800 Thread[17395:417331] end task 1
2018-12-29 17:13:45.714511+0800 Thread[17395:417334] end task 2
2018-12-29 17:13:45.714752+0800 Thread[17395:417245] sendRequest done
2018-12-29 17:13:45.714854+0800 Thread[17395:417245] sendRequest2 done
2.用dispatch_group_enter
+ dispatch_group_leave
實(shí)現(xiàn)
- (void)gcdGroupEnter{
NSLog(@"主線程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self sendRequest:^{
NSLog(@"sendRequest done");
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self sendRequest2:^{
NSLog(@"sendRequest2 done");
dispatch_group_leave(group);
}];
dispatch_group_notify(group, queue, ^{
NSLog(@"all task over");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主線程刷新UI");
});
});
}
輸出結(jié)果:當(dāng)task1 和task2執(zhí)行結(jié)束后渣触,才輸出all task over羡棵,符合我們的需求。
當(dāng)我們調(diào)用sendRequest
時(shí)嗅钻,先調(diào)用dispatch_group_enter
時(shí)皂冰,任務(wù)回調(diào)后再調(diào)用dispatch_group_leave
,整個(gè)異步操作中养篓,任務(wù)是被group
持有的秃流,只有回調(diào)結(jié)束后才離開group
,所以不會(huì)出現(xiàn)上面的問(wèn)題柳弄。
注意:dispatch_group_enter
和dispatch_group_leave
成對(duì)存在
2018-12-29 17:32:12.739361+0800 Thread[17557:425760] 主線程
2018-12-29 17:32:12.739608+0800 Thread[17557:425809] start task 1
2018-12-29 17:32:12.739608+0800 Thread[17557:425810] start task 2
2018-12-29 17:32:15.742987+0800 Thread[17557:425809] end task 1
2018-12-29 17:32:15.742990+0800 Thread[17557:425810] end task 2
2018-12-29 17:32:15.743325+0800 Thread[17557:425760] sendRequest2 done
2018-12-29 17:32:15.743528+0800 Thread[17557:425760] sendRequest done
2018-12-29 17:32:15.743742+0800 Thread[17557:425810] all task over
2018-12-29 17:32:15.744000+0800 Thread[17557:425760] 回到主線程刷新UI
10.dispatch_semaphore(信號(hào)量)
dispatch_semaphore
使用計(jì)數(shù)來(lái)完成這個(gè)問(wèn)題舶胀,計(jì)數(shù)為0 ,不可以通過(guò)碧注,計(jì)數(shù)大于等于1嚣伐,可以通過(guò)
其中有三個(gè)函數(shù)分別為:
-
dispatch_semaphore_create
:創(chuàng)建semaphore
并初始化信號(hào)量,初始化的值大于等于0; -
dispatch_semaphore_signal
:信號(hào)量+1萍丐; -
dispatch_semaphore_wait
: 判斷當(dāng)前信號(hào)量的值轩端,如果當(dāng)前信號(hào)量大于0,信號(hào)量-1逝变,往下執(zhí)行基茵,如果當(dāng)前信號(hào)量等于0,就會(huì)阻塞在當(dāng)前線程壳影,一直等待拱层。
用處:
1、保持線程同步态贤,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)
2舱呻、保證線程安全醋火,為線程加鎖
線程安全
:在多個(gè)線程中同時(shí)訪問(wèn)并操作同一對(duì)象時(shí)悠汽,運(yùn)行結(jié)果與預(yù)期的值相同就是線程安全箱吕。
線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的,若每個(gè)線程中對(duì)全局變量
柿冲、靜態(tài)變量
只有讀操作茬高,而無(wú)寫操作,一般來(lái)說(shuō)假抄,這個(gè)全局變量是線程安全的怎栽;若有多個(gè)線程同時(shí)執(zhí)行寫操作,一般都需要考慮線程同步宿饱,否則的話就可能影響線程安全熏瞄。
線程同步
:可理解為線程A和B一塊配合,A執(zhí)行到一定程度時(shí)要依靠B的某個(gè)結(jié)果谬以,于是停下來(lái)强饮,示意B運(yùn)行;B依言執(zhí)行为黎,再將結(jié)果給A邮丰;A再繼續(xù)操作。
10.1.保持線程同步铭乾,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)
這個(gè)也可以用來(lái)實(shí)現(xiàn)上面的需求1
- (void)gcdSemaphore{
NSLog(@"主線程");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)一剪廉,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
long x = dispatch_semaphore_signal(semaphore);
NSLog(@"signal后的信號(hào)量 = %ld",x);
});
long x = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"wait后的信號(hào)量 = %ld",x);
NSLog(@"---dispatch_semaphore_wait-----");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任務(wù)四炕檩,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
輸出結(jié)果:
- 先輸出任務(wù)一斗蒋,再輸出任務(wù)三、四
-
dispatch_semaphore_wait
在主線程輸出(信號(hào)量為0 笛质,阻塞在當(dāng)前線程)
我們?cè)谥骶€程創(chuàng)建了一個(gè)信號(hào)量賦值為0吹泡,并開辟了一個(gè)并行隊(duì)列異步執(zhí)行任務(wù)一,因?yàn)槭且粋€(gè)異步操作经瓷,此時(shí)不會(huì)等待任務(wù)一執(zhí)行結(jié)束爆哑, 直接執(zhí)行到dispatch_semaphore_wait
,此時(shí)判斷出信號(hào)量的值0舆吮,不可通行揭朝,阻塞當(dāng)前線程,當(dāng)運(yùn)行到dispatch_semaphore_signal
時(shí)色冀,信號(hào)量加1后等于1大于0潭袱,可通行,執(zhí)行dispatch_semaphore_wait
后面的任務(wù)三锋恬、四屯换,通行后信號(hào)量減1等于0。
2018-12-29 15:22:24.960944+0800 Thread[16315:368237] 主線程
2018-12-29 15:22:24.961180+0800 Thread[16315:368286] 任務(wù)一,i = 0
2018-12-29 15:22:25.965568+0800 Thread[16315:368286] 任務(wù)一彤悔,i = 1
2018-12-29 15:22:26.970160+0800 Thread[16315:368286] 任務(wù)一嘉抓,i = 2
2018-12-29 15:22:27.975627+0800 Thread[16315:368237] wait后的信號(hào)量 = 0
2018-12-29 15:22:27.975627+0800 Thread[16315:368286] signal后的信號(hào)量 = 1
2018-12-29 15:22:27.975956+0800 Thread[16315:368237] ---dispatch_semaphore_wait-----
2018-12-29 15:22:27.976232+0800 Thread[16315:368287] 任務(wù)四,i = 0
2018-12-29 15:22:27.976285+0800 Thread[16315:368286] 任務(wù)三晕窑,i = 0
2018-12-29 15:22:28.978431+0800 Thread[16315:368286] 任務(wù)三抑片,i = 1
2018-12-29 15:22:28.978445+0800 Thread[16315:368287] 任務(wù)四,i = 1
2018-12-29 15:22:29.980405+0800 Thread[16315:368286] 任務(wù)三杨赤,i = 2
2018-12-29 15:22:29.980405+0800 Thread[16315:368287] 任務(wù)四敞斋,i = 2
10.2.保證線程安全,為線程加鎖
在上面講NSThread的時(shí)候疾牲,講過(guò)synchronized
和NSCondition
加鎖方式植捎,這里使用dispatch_semaphore
進(jìn)行加密,運(yùn)行結(jié)果和上面一致。
- (void)sale{
while (1) {
//1阳柔、synchronized
// @synchronized (self) {
// if (self.tickets > 0 ) {
// [NSThread sleepForTimeInterval:0.1];
// self.tickets --;
// self.saleCount = Total - self.tickets;
// NSLog(@"%@ , 賣出 = %d鸥跟,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
// }else{
// break;//一定要break,不然就會(huì)死循環(huán)
// }
// }
// 2盔沫、NSCondition
// [self.condition lock];
// if (self.tickets > 0 ) {
// [NSThread sleepForTimeInterval:0.1];
// self.tickets --;
// self.saleCount = Total - self.tickets;
// NSLog(@"%@ , 賣出 = %d医咨,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
// }else{
// break;
// }
// [self.condition unlock];
//
//3、dispatch_semaphore方式
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
if (self.tickets > 0 ) {
[NSThread sleepForTimeInterval:0.1];
self.tickets --;
self.saleCount = Total - self.tickets;
NSLog(@"%@ , 賣出 = %d架诞,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
}else{
dispatch_semaphore_signal(self.semaphore);
break;
}
dispatch_semaphore_signal(self.semaphore);
}
}
信號(hào)量還需要多看點(diǎn)資料拟淮,這里就先這樣子吧~~
上面就保持線程同步,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)谴忧,保證線程安全很泊,給線程加鎖就講了好多種方式,選擇的時(shí)候沾谓,針對(duì)需求而言來(lái)選擇一個(gè)較好的方式就OK啦~
參考博客:
iOS多線程慕課網(wǎng)視頻
深入理解iOS開發(fā)中的鎖
文章鏈接:
iOS 多線程- pThread和NSThread
iOS 多線程-NSOperation + NSOperationQueue
喜歡就點(diǎn)個(gè)贊吧????
有錯(cuò)之處委造,還請(qǐng)指出,感謝????