本文內(nèi)容
任務(wù)、隊列的概念、創(chuàng)建方式
任務(wù) + 隊列的6種組合的執(zhí)行方式
線程間如何通信
dispatch_once、dispatch_after、dispatch_apply(快速迭代)、dispatch_barrier(柵欄函數(shù))、dispatch_group(隊列組)、dispatch_semaphore(信號量)如何實現(xiàn)線程安全與線程同步
iOS多線程demo地址
上文說到iOS 多線程- pThread和NSThread
這篇文章來講講GCD
GCD ??????????的優(yōu)點
- 可用于多核的并行運算
- 會自動利用更多的CPU內(nèi)核
- 自動管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
- 只用關(guān)注執(zhí)行什么任務(wù),不用編寫任何線程管理代碼
1.任務(wù)
任務(wù):執(zhí)行的操作,放在block中的代碼
執(zhí)行任務(wù)的方式有兩種,主要區(qū)別是:是否等待隊列中的任務(wù)執(zhí)行結(jié)束,是否具備開啟新線程的能力。
同步執(zhí)行(sync):同步添加當前任務(wù)到指定的隊列中,在隊列中的任務(wù)全部結(jié)束之前,
會一直等待,直到隊列中的任務(wù)全部完成后,才開始下一個任務(wù),只能在當前線程中執(zhí)行任務(wù),不具備開啟線程的能力。異步執(zhí)行 (async):異步添加當前任務(wù)到指定隊列中,
不會等待隊列的任務(wù)執(zhí)行結(jié)束,直接開始執(zhí)行下一個任務(wù),可以在新的線程中執(zhí)行任務(wù),具備開啟線程的能力。
*注意:異步執(zhí)行 (async)雖然具有開啟線程的能力,但是不一定會開啟新的線程,這跟任務(wù)所指定的隊列有關(guān)
2.隊列
隊列:存放任務(wù)的隊列,隊列是一種特殊的線性表,采用FIFO(先進先出)的原則,即新任務(wù)總是被插入到隊列末尾,而讀取任務(wù)總是從隊列的頭部開始讀取,每讀取一個任務(wù),隊列中則釋放一個任務(wù)。
隊列有兩種方式,都滿足FIFO原則,主要區(qū)別是:執(zhí)行順序不同,開啟線程數(shù)不同
- 串行隊列(Serial Dispatch Queue ):只開啟
一個線程,一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù) - 并行隊列(Concurrent Dispatch Queue)?????????????? ??????????????????????????????????????????????????????????????:可以開啟
多個線程,并且同時執(zhí)行多個任務(wù)
3.使用步驟
- 創(chuàng)建一個隊列
- 將任務(wù)添加到隊列中,系統(tǒng)根據(jù)任務(wù)執(zhí)行方式(同步、異步)進行執(zhí)行
3.1 隊列的創(chuàng)建、獲取
使用dispatch_queue_create創(chuàng)建隊列
第一個參數(shù):隊列的唯一標識符,用于DEBUG,可以為空,推薦使用應(yīng)用程序ID這種逆序全局域名。
第二個參數(shù):隊列類型,串行隊列DISPATCH_QUEUE_SERIAL,并行隊列DISPATCH_QUEUE_CONCURRENT
//創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.queque", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建并行隊列
dispatch_queue_t queue2 = dispatch_queue_create("com.xiuxiu.queque", DISPATCH_QUEUE_CONCURRENT);
主隊列Main Dispatch Queue: GCD提供一種特殊串行隊列:
- 所有放在主隊列中的任務(wù),都會放到主線程中執(zhí)行
- 可使用
dispatch_get_main_queue()獲取主隊列
主隊列獲取方法
dispatch_queue_t queue = dispatch_get_main_queue()
全局并發(fā)隊列Global Dispatch Queue : GCD默認提供的全局并發(fā)隊列
并發(fā)隊列獲取方法:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_get_global_queue獲取全局隊列
第一個參數(shù):隊列的優(yōu)先級
#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
第二個參數(shù):沒有使用,用0 即可
3.2 任務(wù)的執(zhí)行方式
dispatch_async(queue, ^{
//異步執(zhí)行任務(wù)代碼
});
dispatch_sync(queue, ^{
//同步執(zhí)行任務(wù)代碼
});
第一個參數(shù)是隊列,那么隊列 + 任務(wù) 執(zhí)行方式就有6種組合(加上主隊列)
同步執(zhí)行 + 串行隊列
異步執(zhí)行 + 串行隊列
同步執(zhí)行 + 并行隊列
異步執(zhí)行 + 并行隊列
同步執(zhí)行 + 主隊列
異步執(zhí)行 + 主隊列
3.2.1.同步執(zhí)行 + 串行隊列
/*
同步執(zhí)行 + 串行隊列
不會開啟新線程,在當前線程中執(zhí)行任務(wù),一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(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");
}
:前面的編號代表進程的編號,一個APP的就是一個進程,進程編號總是一致的;
:后面的編號代表線程的編號, 21898代表線程的編號
輸出結(jié)果:
- 在當前線程中執(zhí)行任務(wù),沒有開啟新線程(同步執(zhí)行不具備開啟線程能力),根據(jù)線程編號看出
- 所有任務(wù)都在
syncSerial start和syncSerial end之間執(zhí)行(同步執(zhí)行需要等待隊列中的任務(wù)執(zhí)行結(jié)束) - 任務(wù)按順序執(zhí)行(串行隊列每次只有一個任務(wù)被執(zhí)行,一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(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í)行 + 串行隊列
/*
異步執(zhí)行 + 串行隊列
會開啟新線程,但是因為隊列是串行的,一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(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í)行具備開啟線程的能力,因為是串行隊列,只開啟了一條線程)
- 所有任務(wù)都在
asyncSerial start和asyncSerial end之后執(zhí)行(異步執(zhí)行不會等待,繼續(xù)執(zhí)行任務(wù)) - 任務(wù)按順序執(zhí)行(串行隊列每次只有一個任務(wù)被執(zhí)行,任務(wù)一個接著一個執(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í)行 + 并行隊列
/*
同步執(zhí)行 + 并行隊列
不會開啟新線程,在當前線程中執(zhí)行任務(wù),一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(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é)果:
- 在當前線程中執(zhí)行任務(wù),沒有開啟新線程(同步執(zhí)行不具備開啟線程能力),根據(jù)線程編號看出,上面講過
- 所有任務(wù)都在
syncConcurrent start和syncConcurrent end之間執(zhí)行(同步執(zhí)行需要等待隊列中的任務(wù)執(zhí)行結(jié)束) - 任務(wù)按順序執(zhí)行,雖然并行隊列可以開啟多個線程,并且同時執(zhí)行多個任務(wù),但是因為同步執(zhí)行不具備開啟線程的能力,只有當前這一個線程,而且同步執(zhí)行需要等待隊列中的任務(wù)執(zhí)行結(jié)束,再執(zhí)行下一個任務(wù),所以任務(wù)只能一個接一個按照順序執(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ā)隊列
/*
異步執(zhí)行 + 并行隊列
開啟多個線程,任務(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個新線程(異步執(zhí)行具備開啟線程的能力)
- 所有任務(wù)都在
asyncConcurrent start和asyncConcurrent end之后執(zhí)行(異步執(zhí)行不會等待,繼續(xù)執(zhí)行任務(wù)) - 任務(wù)交替執(zhí)行(異步執(zhí)行具備開啟線程的能力,并且并行隊列可以開啟多個線程,同時執(zhí)行多個任務(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í)行 + 主隊列
同步執(zhí)行 + 主隊列 在不同線程中調(diào)用,結(jié)果不一樣。
在主線程中調(diào)用,出現(xiàn)死鎖
在其他線程中調(diào)用,不會開啟線程,一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù)
3.2.5.1在主線程調(diào)用
/*
同步執(zhí)行 + 主隊列
在主線程中調(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 上運行崩潰,只輸出了syncMain start
為什么?
因為我們在主線程中執(zhí)行 syncMain方法,相當于把 syncMain任務(wù)放到主線程的隊列中,而同步執(zhí)行會等待當前隊列中的任務(wù)執(zhí)行完畢后,才會接著執(zhí)行。我們把任務(wù)一追加到主隊列中,任務(wù)一會等待主線程處理完syncMain方法,而syncMain方法又需要等待任務(wù)一執(zhí)行完畢,才能繼續(xù)執(zhí)行,雙方都在等待,所以線程死鎖,任務(wù)無法執(zhí)行。
2018-12-27 11:26:17.011738+0800 Thread[1443:50531] syncMain start
3.2.5.2 在其他線程調(diào)用
/*
同步執(zhí)行 + 主隊列
在其他線程中調(diào)用,不會開啟線程,一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(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é)果:
- 沒有開啟線程,放在主隊列的任務(wù)都在主線程中執(zhí)行
- 所有任務(wù)都在
syncMain start和syncMain end之間執(zhí)行(同步執(zhí)行需要等待隊列中的任務(wù)執(zhí)行結(jié)束) - 任務(wù)按順序執(zhí)行(串行隊列每次只有一個任務(wù)被執(zhí)行,任務(wù)一個接著一個執(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í)行 + 主隊列
/*
異步執(zhí)行 + 主隊列
不會開啟新線程,在主線程中執(zhí)行,一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(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é)果:
- 沒有開啟線程,放在主隊列的任務(wù)都在主線程中執(zhí)行
- 所有任務(wù)都在
asyncMain start和asyncMain end之后執(zhí)行(異步執(zhí)行不會等待,繼續(xù)執(zhí)行任務(wù)) - 任務(wù)按順序執(zhí)行(因為主隊列是串行隊列,每次只有一個任務(wù)被執(zhí)行,任務(wù)一個接著一個執(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ā)工程中,我們一般在主線程中進行UI刷新,如:點擊、拖拽、滾動事件,耗時操作放在其他線程中,而當耗時操作結(jié)束后,回到主線程,就需要用到線程間的通信。
在全局隊列中執(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)建單例或者整個程序只運行一次的代碼,可以使用dispatch_once,dispatch_once函數(shù)保證這個程序運行過程中只被執(zhí)行一次,即時在多線程情況下也是安全的。
/*
驗證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)建對象");
});
}
輸出結(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)建對象
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并不是在指定時間之后才執(zhí)行處理,而是在指定時間之后將任務(wù)追加到隊列中,這個指定時間并不是絕對準確的,想要大致完成延時任務(wù)可以使用dispatch_after函數(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ù)添加到隊列中,并等待隊列中的任務(wù)全部執(zhí)行結(jié)束。
如果在串行隊列中使用dispatch_apply函數(shù),就和for循環(huán)遍歷一樣,按照順序執(zhí)行,體現(xiàn)不出快速迭代的意義。
如果在并行隊列中使用dispatch_apply函數(shù),dispatch_apply可以在多個線程中同時遍歷多個數(shù)字。
7.1 在串行隊列使用dispatch_apply
將在主隊列dispatch_get_main_queue的遍歷任務(wù)放在并行隊列dispatch_get_global_queue中,為了避免上面講的死鎖問題,關(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é)果:
- 沒有開啟線程(主隊列的任務(wù)只在主線程中執(zhí)行)
-
apply end在最后輸出(dispatch_apply函數(shù)會等待隊列全部任務(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在并行隊列使用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é)果:
- 開啟線程(看線程編號得出)
-
apply end在最后輸出(dispatch_apply函數(shù)會等待隊列全部任務(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í)行兩個操作(一個操作可以是一個任務(wù),也可以是多個任務(wù),這里是兩個任務(wù)),而且第一組操作結(jié)束后,才能開始第二組操作,如何實現(xiàn)呢?
這里的意思其實保持線程同步,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)的意思。
方法1:使用柵欄函數(shù)
方法2:使用dispatch_group
方式3 :使用dispatch_semaphore
這三個方法都會在下面一一講解的。
總結(jié):就當前這個需求,使用柵欄函數(shù)會比較簡單,所有方法講完具體實現(xiàn)就可以看出來了不用創(chuàng)建group或者semaphore,直接放個柵欄在中間,分隔兩個操作。
8.dispatch_barrier(柵欄函數(shù))
柵欄函數(shù)存在的意義:先執(zhí)行柵欄函數(shù)之前的任務(wù),再執(zhí)行柵欄函數(shù)中的任務(wù),最后執(zhí)行柵欄函數(shù)之后的任務(wù),增加柵欄函數(shù)不影響原有隊列的任務(wù)執(zhí)行方式,也就是柵欄函數(shù)之前隊列的任務(wù)是什么執(zhí)行方式,柵欄函數(shù)之后隊列的任務(wù)還是什么執(zhí)行方式。
柵欄函數(shù)分為:dispatch_barrier_async 和dispatch_barrier_sync
區(qū)別就是 :添加柵欄函數(shù)后面任務(wù)到隊列的時間不一樣。
dispatch_barrier_sync:需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束后,才會添加柵欄函數(shù)后的任務(wù)到隊列中。
dispatch_barrier_async : 不需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束,就已經(jīng)將柵欄函數(shù)后的任務(wù)到隊列中。
8.1 dispatch_barrier_sync
實現(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ù)不影響隊列任務(wù)的執(zhí)行方式;
- 橫線在柵欄函數(shù)之后輸出(
dispatch_barrier_sync:需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束后,才會添加柵欄函數(shù)后的任務(wù)到隊列中)。
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
實現(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ù)不影響隊列任務(wù)的執(zhí)行方式;
- 橫線在柵欄函數(shù)之前輸出(
dispatch_barrier_async:不需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束,就已經(jīng)將柵欄函數(shù)后的任務(wù)到隊列中)。
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(隊列組)
任務(wù)的執(zhí)行是先創(chuàng)建一個任務(wù),放入隊列進行執(zhí)行。而隊列組就是用來存放隊列的一個組。
將隊列放入隊列組中可以使用 dispatch_group_async 或者dispatch_group_enter 和dispatch_group_leave的組合實現(xiàn)。
隊列組的任務(wù)結(jié)束完成后,調(diào)用dispatch_group_notify 可以回到指定線程執(zhí)行任務(wù),調(diào)用dispatch_group_wait可以回到當前線程執(zhí)行任務(wù)(阻塞當前線程)。
就上面的需求1,用dispatch_group方式實現(xiàn)
1. 我們用dispatch_group_async + dispatch_group_notify來進行實現(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來進行實現(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在主線程輸出,就如之前所說dispatch_group_wait會阻塞當前線程。
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來進行實現(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來進行實現(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在主線程輸出,就如之前所說dispatch_group_wait會阻塞當前線程
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í)行兩個網(wǎng)絡(luò)請求,兩個網(wǎng)絡(luò)請求執(zhí)行結(jié)束后,進行一定的操作。
這里sendRequest用來代表網(wǎng)絡(luò)請求
- (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實現(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還沒有結(jié)束,就輸出all task over 咯,為啥子呢?
原因:因為dispatch_group_async里面放入的是異步的任務(wù),dispatch_group_async執(zhí)行了sendRequest這行代碼后,就認為sendRequest已經(jīng)執(zhí)行完畢了(其實還沒有回調(diào)回來),group不再持有這個任務(wù),就會執(zhí)行下面的dispatch_group_async,而sendRequest2同理,group沒有任務(wù)時,就會執(zhí)行dispatch_group_notify 里面的任務(wù),所以造成這樣子的輸出結(jié)果。
由此可見: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 實現(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é)果:當task1 和task2執(zhí)行結(jié)束后,才輸出all task over,符合我們的需求。
當我們調(diào)用sendRequest時,先調(diào)用dispatch_group_enter時,任務(wù)回調(diào)后再調(diào)用dispatch_group_leave,整個異步操作中,任務(wù)是被group持有的,只有回調(diào)結(jié)束后才離開group,所以不會出現(xiàn)上面的問題。
注意:dispatch_group_enter和dispatch_group_leave成對存在
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(信號量)
dispatch_semaphore 使用計數(shù)來完成這個問題,計數(shù)為0 ,不可以通過,計數(shù)大于等于1,可以通過
其中有三個函數(shù)分別為:
-
dispatch_semaphore_create:創(chuàng)建semaphore并初始化信號量,初始化的值大于等于0; -
dispatch_semaphore_signal:信號量+1; -
dispatch_semaphore_wait: 判斷當前信號量的值,如果當前信號量大于0,信號量-1,往下執(zhí)行,如果當前信號量等于0,就會阻塞在當前線程,一直等待。
用處:
1、保持線程同步,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)
2、保證線程安全,為線程加鎖
線程安全:在多個線程中同時訪問并操作同一對象時,運行結(jié)果與預(yù)期的值相同就是線程安全。
線程安全問題都是由全局變量及靜態(tài)變量引起的,若每個線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。
線程同步:可理解為線程A和B一塊配合,A執(zhí)行到一定程度時要依靠B的某個結(jié)果,于是停下來,示意B運行;B依言執(zhí)行,再將結(jié)果給A;A再繼續(xù)操作。
10.1.保持線程同步,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)
這個也可以用來實現(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后的信號量 = %ld",x);
});
long x = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"wait后的信號量 = %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在主線程輸出(信號量為0 ,阻塞在當前線程)
我們在主線程創(chuàng)建了一個信號量賦值為0,并開辟了一個并行隊列異步執(zhí)行任務(wù)一,因為是一個異步操作,此時不會等待任務(wù)一執(zhí)行結(jié)束, 直接執(zhí)行到dispatch_semaphore_wait,此時判斷出信號量的值0,不可通行,阻塞當前線程,當運行到dispatch_semaphore_signal時,信號量加1后等于1大于0,可通行,執(zhí)行dispatch_semaphore_wait后面的任務(wù)三、四,通行后信號量減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后的信號量 = 0
2018-12-29 15:22:27.975627+0800 Thread[16315:368286] signal后的信號量 = 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的時候,講過synchronized和NSCondition加鎖方式,這里使用dispatch_semaphore進行加密,運行結(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á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);
}
}
信號量還需要多看點資料,這里就先這樣子吧~~
上面就保持線程同步,將異步任務(wù)轉(zhuǎn)化為同步任務(wù),保證線程安全,給線程加鎖就講了好多種方式,選擇的時候,針對需求而言來選擇一個較好的方式就OK啦~
參考博客:
iOS多線程慕課網(wǎng)視頻
深入理解iOS開發(fā)中的鎖
文章鏈接:
iOS 多線程- pThread和NSThread
iOS 多線程-NSOperation + NSOperationQueue
喜歡就點個贊吧????
有錯之處,還請指出,感謝????