問(wèn)題
主要是runloop的原理以及核心源代碼
gcd的原理及應(yīng)用,source的原理為什么比timer更精準(zhǔn);
定義
GCD是異步執(zhí)行任務(wù)的技術(shù)之一;一般將應(yīng)用程序中記述的線(xiàn)程管理利用的代碼在系統(tǒng)級(jí)中實(shí)現(xiàn)。
開(kāi)發(fā)者只需要定義想要執(zhí)行的任務(wù)并追加到適當(dāng)?shù)?Dispatch Queue中,GCD就能生成必要的線(xiàn)程并計(jì)劃執(zhí)行任務(wù)
熟悉了解一下NSThread
-
特點(diǎn):
- 1)使用更加面向?qū)ο?/li>
- 2)簡(jiǎn)單易用,可直接操作線(xiàn)程對(duì)象
- (void)test_NSThread
{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"1)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run0) object:nil];
[thread start]; //必須手動(dòng)開(kāi)啟
NSLog(@"2)是否是主線(xiàn)程 %d - %@",[thread isMainThread],thread);
//創(chuàng)建線(xiàn)程后自動(dòng)啟動(dòng)線(xiàn)程
[NSThread detachNewThreadSelector:@selector(run1) toTarget:self withObject:nil];
//隱式創(chuàng)建并啟動(dòng)線(xiàn)程
[self performSelectorInBackground:@selector(run2) withObject:nil];
//執(zhí)行后臺(tái)程序
[self performSelectorInBackground:@selector(doWork) withObject:nil];
}
- (void)run0
{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"3)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
}
- (void)run1
{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"4)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
}
- (void)run2
{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"5)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
}
- (void)doWork
{
sleep(5);
//長(zhǎng)時(shí)間處理結(jié)束,主線(xiàn)成使用其他處理
[self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO];
}
- (void)doneWork
{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"6)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
}
輸出
2019-01-15 13:30:02.439991+0800 GCD[1211:22656] 1)是否是主線(xiàn)程 1 - <NSThread: 0x6000034aa900>{number = 1, name = main}
2019-01-15 13:30:02.440236+0800 GCD[1211:22656] 2)是否是主線(xiàn)程 0 - <NSThread: 0x6000034cca80>{number = 3, name = main}
2019-01-15 13:30:02.441207+0800 GCD[1211:22695] 4)是否是主線(xiàn)程 0 - <NSThread: 0x6000034cc940>{number = 4, name = (null)}
2019-01-15 13:30:02.442023+0800 GCD[1211:22694] 3)是否是主線(xiàn)程 0 - <NSThread: 0x6000034cca80>{number = 3, name = (null)}
2019-01-15 13:30:02.444018+0800 GCD[1211:22696] 5)是否是主線(xiàn)程 0 - <NSThread: 0x6000034ccac0>{number = 5, name = (null)}
2019-01-15 13:30:07.470940+0800 GCD[1211:22656] 6)是否是主線(xiàn)程 1 - <NSThread: 0x6000034aa900>{number = 1, name = main}
由于使用多線(xiàn)程的程序可以在某個(gè)線(xiàn)程和其他線(xiàn)程之間反復(fù)多次進(jìn)行上下文切換,因此,看上去就像一個(gè)cpu核能夠并發(fā)的執(zhí)行多個(gè)線(xiàn)程一樣;
但是,多線(xiàn)程編程實(shí)際上是一種易發(fā)生各種問(wèn)題的編程技術(shù)。比如 多個(gè)線(xiàn)程更新相同的資源會(huì)導(dǎo)致數(shù)據(jù)不一樣(數(shù)據(jù)競(jìng)爭(zhēng))、停止等待事件的線(xiàn)程會(huì)導(dǎo)致多個(gè)線(xiàn)程互相持續(xù)等待(死鎖)、使用太多線(xiàn)程會(huì)消耗大量的內(nèi)存
應(yīng)用程序在啟動(dòng)的時(shí)候,通過(guò)最先執(zhí)行的線(xiàn)程,即
主線(xiàn)程來(lái)描繪用戶(hù)界面、處理觸摸屏幕的事件等等。如果在該主線(xiàn)程中進(jìn)行長(zhǎng)時(shí)間的處理,比如:圖像識(shí)別、數(shù)據(jù)庫(kù)訪(fǎng)問(wèn) 就會(huì)妨礙主線(xiàn)程的執(zhí)行(阻塞)。
會(huì)妨礙主線(xiàn)程中被稱(chēng)為RunLoop的主循環(huán)的執(zhí)行,從而導(dǎo)致不能更新用戶(hù)界面,應(yīng)用程序的畫(huà)面長(zhǎng)時(shí)間停滯等問(wèn)題
使用多線(xiàn)程編程,在執(zhí)行長(zhǎng)時(shí)間的處理時(shí)可以保證用戶(hù)界面的響應(yīng)性能
GCD的API
開(kāi)發(fā)者只需要定義想要執(zhí)行的任務(wù)并追加到適當(dāng)?shù)?Dispatch Queue中,GCD就能生成必要的線(xiàn)程并計(jì)劃執(zhí)行任務(wù)
dispatch_async(queue , ^{
//想要執(zhí)行的任務(wù)
});
-
dispatch queue 如其名所示,是
執(zhí)行處理的等待隊(duì)列
執(zhí)行處理的等待隊(duì)列 分為2種:
| dispatch queue種類(lèi) | 說(shuō)明 |
|---|---|
| serial Dispatch Queue | 等待現(xiàn)在執(zhí)行中處理結(jié)束 |
| Concurrent Dispatch Queue | 不等待現(xiàn)在執(zhí)行中處理結(jié)束 |
dispatch queue 創(chuàng)建
代碼查看
//dispatch_queue_t dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);
//參數(shù)一:指定SerialDispatchQueue 的名稱(chēng),可以為NULL
//參數(shù)二:生成SerialDispatchQueue 可以指定為NULL,生成Concurrent Dispatch Queue 指定為:DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", NULL);
dispatch_queue_t my ConcurrentlDispatchQueue = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", DISPATCH_QUEUE_CONCURRENT);
- (void)serialDispatch
{
NSLog(@"主線(xiàn)程----%@",[NSThread mainThread]);
//參數(shù)一:指定SerialDispatchQueue 的名稱(chēng),可以為NULL
//參數(shù)二:生成SerialDispatchQueue 可以指定為NULL,生成Concurrent Dispatch Queue 指定為:DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", NULL);
//向隊(duì)列中添加任務(wù)
//在block語(yǔ)法中記述想執(zhí)行的處理并將其追加到dispatch Queue中
dispatch_async(mySerialDispatchQueue, ^{
NSLog(@"下載圖片1----%@",[NSThread currentThread]);
});
dispatch_async(mySerialDispatchQueue, ^{
NSLog(@"下載圖片2----%@",[NSThread currentThread]);
});
dispatch_async(mySerialDispatchQueue, ^{
NSLog(@"下載圖片3----%@",[NSThread currentThread]);
});
dispatch_async(mySerialDispatchQueue, ^{
NSLog(@"下載圖片4----%@",[NSThread currentThread]);
});
//必須要手動(dòng)釋放
//名稱(chēng)中含有“release”的api在不需要生成的對(duì)象時(shí),有必要通過(guò)dispatch_release函數(shù)進(jìn)行釋放
//如果你部署的最低目標(biāo)是 iOS 6.0 or Mac OS X 10.8 或者更高的
//ARC已經(jīng)能夠管理GCD對(duì)象了,這時(shí)候,GCD對(duì)象就如同普通的OC對(duì)象一樣,不應(yīng)該使用dispatch_retain ordispatch_release
//dispatch_release(mySerialDispatchQueue);
}
打印
2019-01-15 13:56:47.280855+0800 GCD[1434:32459] 主線(xiàn)程----<NSThread: 0x600001b31400>{number = 1, name = main}
2019-01-15 13:56:47.281106+0800 GCD[1434:32500] 下載圖片1----<NSThread: 0x600001b6a940>{number = 3, name = (null)}
2019-01-15 13:56:47.281242+0800 GCD[1434:32500] 下載圖片2----<NSThread: 0x600001b6a940>{number = 3, name = (null)}
2019-01-15 13:56:47.281375+0800 GCD[1434:32500] 下載圖片3----<NSThread: 0x600001b6a940>{number = 3, name = (null)}
2019-01-15 13:56:47.281511+0800 GCD[1434:32500] 下載圖片4----<NSThread: 0x600001b6a940>{number = 3, name =
總結(jié):serial Dispatch Queue (等待現(xiàn)在執(zhí)行中處理結(jié)束),使用的是一個(gè)線(xiàn)程,一個(gè)一個(gè)執(zhí)行
當(dāng)隊(duì)列改為Concurrent Dispatch Queue,
//參數(shù)一:指定SerialDispatchQueue 的名稱(chēng),可以為NULL
//參數(shù)二:生成SerialDispatchQueue 可以指定為NULL,生成Concurrent Dispatch Queue 指定為:DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", DISPATCH_QUEUE_CONCURRENT);
輸出如下:
2019-01-15 14:03:17.159117+0800 GCD[1514:35799] 主線(xiàn)程----<NSThread: 0x6000021be940>{number = 1, name = main}
2019-01-15 14:03:17.159437+0800 GCD[1514:35836] 下載圖片1----<NSThread: 0x6000021edb40>{number = 3, name = (null)}
2019-01-15 14:03:17.159444+0800 GCD[1514:35833] 下載圖片4----<NSThread: 0x6000021edc00>{number = 6, name = (null)}
2019-01-15 14:03:17.159463+0800 GCD[1514:35835] 下載圖片2----<NSThread: 0x6000021edbc0>{number = 5, name = (null)}
2019-01-15 14:03:17.159467+0800 GCD[1514:35834] 下載圖片3----<NSThread: 0x6000021edb00>{number = 4, name = (null)}
2019-01-15 14:03:17.159595+0800 GCD[1514:35836] 下載圖片5----<NSThread: 0x6000021edb40>{number = 3, name = (null)}
2019-01-15 14:03:17.159623+0800 GCD[1514:35833] 下載圖片6----<NSThread: 0x6000021edc00>{number = 6, name = (null)}
2019-01-15 14:03:17.159681+0800 GCD[1514:35835] 下載圖片7----<NSThread: 0x6000021edbc0>{number = 5, name = (null)}
2019-01-15 14:03:17.159704+0800 GCD[1514:35834] 下載圖片8----<NSThread: 0x6000021edb00>{number = 4, name = (null)}
總結(jié):Concurrent Dispatch Queue,不等待現(xiàn)在執(zhí)行中處理結(jié)束,使用的是多個(gè)線(xiàn)程,并發(fā)的執(zhí)行
注意:
雖然一個(gè)serial Dispatch Queue 同時(shí)只能執(zhí)行一個(gè)追加處理(一個(gè)線(xiàn)程),如果多個(gè)serial Dispatch Queue(多個(gè)線(xiàn)程) 將并發(fā)執(zhí)行【前提是沒(méi)有改變優(yōu)先級(jí),代碼使用dispatch_set_target_queue 有介紹】
serial Dispatch Queue 使用一個(gè)線(xiàn)程時(shí),數(shù)據(jù)是安全的
/**
* 總結(jié):會(huì)開(kāi)啟線(xiàn)程,但是只開(kāi)啟一個(gè)線(xiàn)程,同步執(zhí)行
* 當(dāng)生成多個(gè)serial Dispatch Queue時(shí),各個(gè)serial Dispatch Queue將并行執(zhí)行
*/
- (void)serialDispatch02
{
NSLog(@"主線(xiàn)程----%@",[NSThread mainThread]);
//參數(shù)一:指定SerialDispatchQueue 的名稱(chēng),可以為NULL
//參數(shù)二:生成SerialDispatchQueue 可以指定為NULL,生成Concurrent Dispatch Queue 指定為:DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", NULL);
dispatch_queue_t mySerialDispatchQueue2 = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", NULL);
dispatch_async(mySerialDispatchQueue, ^{
NSLog(@"下載圖片1----%@",[NSThread currentThread]);
});
dispatch_async(mySerialDispatchQueue2, ^{
NSLog(@"下載圖片2----%@",[NSThread currentThread]);
});
dispatch_async(mySerialDispatchQueue2, ^{
NSLog(@"下載圖片3----%@",[NSThread currentThread]);
});
dispatch_async(mySerialDispatchQueue, ^{
NSLog(@"下載圖片4----%@",[NSThread currentThread]);
});
}
輸出
2019-01-15 14:11:00.976494+0800 GCD[1579:38823] 主線(xiàn)程----<NSThread: 0x60000027d400>{number = 1, name = main}
2019-01-15 14:11:00.976780+0800 GCD[1579:38869] 下載圖片1----<NSThread: 0x600000229640>{number = 4, name = (null)}
2019-01-15 14:11:00.976780+0800 GCD[1579:38871] 下載圖片2----<NSThread: 0x600000216540>{number = 3, name = (null)}
2019-01-15 14:11:00.976956+0800 GCD[1579:38871] 下載圖片3----<NSThread: 0x600000216540>{number = 3, name = (null)}
2019-01-15 14:11:00.977072+0800 GCD[1579:38869] 下載圖片4----<NSThread: 0x600000229640>{number = 4, name = (null)}
全局隊(duì)列queue、主隊(duì)列queue
Main Dispatch Queue 在主線(xiàn)程執(zhí)行的Dispatch Queue,因?yàn)橹鳜F(xiàn)場(chǎng)只有一個(gè),所以Main Dispatch Queue 自然就是serialDispatch queue,串行執(zhí)行,
- (void)mainDispatchQueue
{
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
dispatch_async(mainDispatchQueue, ^{
NSLog(@"在mainDispatchQueue 里面,當(dāng)前現(xiàn)場(chǎng)---%@",[NSThread currentThread]);
NSLog(@"在mainDispatchQueue 里面,主線(xiàn)程----%@",[NSThread mainThread]);
});
}
輸出如下: 都是主線(xiàn)程
2019-01-15 14:21:18.661956+0800 GCDDemo01[1682:42652] 在mainDispatchQueue 里面,當(dāng)前現(xiàn)場(chǎng)---<NSThread: 0x600000cd9400>{number = 1, name = main}
2019-01-15 14:21:18.662125+0800 GCDDemo01[1682:42652] 在mainDispatchQueue 里面,主線(xiàn)程----<NSThread: 0x600000cd9400>{number = 1, name = main}
全局隊(duì)列 Global Dispatch Queu:block塊里面是并行執(zhí)行的
4個(gè)優(yōu)先級(jí) 如下:
| Global Dispatch Queue種類(lèi) | 說(shuō)明 |
|---|---|
| DISPATCH_QUEUE_PRIORITY_HIGH | 執(zhí)行優(yōu)先級(jí): 最高 |
| DISPATCH_QUEUE_PRIORITY_DEFAULT | 執(zhí)行優(yōu)先級(jí): 默認(rèn) |
| DISPATCH_QUEUE_PRIORITY_LOW | 執(zhí)行優(yōu)先級(jí): 低 |
| DISPATCH_QUEUE_PRIORITY_BACKGROUND | 執(zhí)行優(yōu)先級(jí): 后臺(tái) |
創(chuàng)建方法
//第二個(gè)參數(shù) 都是0
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(globalDispatchQueue, ^{
NSLog(@"當(dāng)前現(xiàn)場(chǎng)---%@",[NSThread currentThread]);
NSLog(@"主線(xiàn)程----%@",[NSThread mainThread]);
//可并行執(zhí)行的處理
//...
dispatch_async(mainDispatchQueue, ^{
//只能在主線(xiàn)程中執(zhí)行的處理
//...
[self.btn setTitle:@"load..." forState:UIControlStateNormal];
});
});
打印如下:
2019-01-15 14:32:22.606748+0800 GCDDemo01[1752:46356] 當(dāng)前現(xiàn)場(chǎng)---<NSThread: 0x600000bc2600>{number = 3, name = (null)}
2019-01-15 14:32:22.607040+0800 GCDDemo01[1752:46356] 主線(xiàn)程----<NSThread: 0x600000ba6940>{number = 1, name = (null)}
變更優(yōu)先級(jí) dispatch_set_target_queue
void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t _Nullable queue);
第一個(gè)參數(shù)為要設(shè)置優(yōu)先級(jí)的queue,第二個(gè)參數(shù)是參照物,既將第一個(gè)queue的優(yōu)先級(jí)和第二個(gè)queue的優(yōu)先級(jí)設(shè)置一樣。
dispatch_queue_create函數(shù)生成的DisPatch Queue不管是Serial DisPatch Queue還是Concurrent Dispatch Queue,執(zhí)行的優(yōu)先級(jí)都與默認(rèn)優(yōu)先級(jí)的Global Dispatch queue相同,
如果需要變更生成的Dispatch Queue的執(zhí)行優(yōu)先級(jí)則需要使用dispatch_set_target_queue函數(shù)
- 變更優(yōu)先級(jí)
//串行隊(duì)列
dispatch_queue_t serialDiapatchQueue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_SERIAL);
//全局隊(duì)列
dispatch_queue_t dispatchgetglobalqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//使串行隊(duì)列 與全局隊(duì)列優(yōu)先級(jí)low 相同
dispatch_set_target_queue(serialDiapatchQueue, dispatchgetglobalqueue);
dispatch_async(serialDiapatchQueue, ^{
NSLog(@"我優(yōu)先級(jí)低,先讓讓");
});
// serialDiapatchQueue 優(yōu)先級(jí)< 新的全局隊(duì)列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"我優(yōu)先級(jí)高,我先block");
});
打印
2019-01-15 14:49:46.603854+0800 GCDDemo01[1874:51890] 我優(yōu)先級(jí)高,我先block
2019-01-15 14:49:46.603884+0800 GCDDemo01[1874:51891] 我優(yōu)先級(jí)低,先讓讓
- 使用dispatch_set_target_queue將多個(gè)串行的queue指定到了同一目標(biāo),那么著多個(gè)串行queue在目標(biāo)queue上就是同步執(zhí)行的,不再是并行執(zhí)行。
//1.創(chuàng)建目標(biāo)隊(duì)列
dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
//2.創(chuàng)建3個(gè)串行隊(duì)列
dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
//3.將3個(gè)串行隊(duì)列分別添加到目標(biāo)隊(duì)列
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"1 in");
[NSThread sleepForTimeInterval:3.f];
NSLog(@"1 out");
});
dispatch_async(queue2, ^{
NSLog(@"2 in");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"2 out");
});
dispatch_async(queue3, ^{
NSLog(@"3 in");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3 out");
});
打印如下: 不再是并行執(zhí)行。
2019-01-15 14:58:52.260714+0800 GCDDemo01[1945:55274] 1 in
2019-01-15 14:58:55.266195+0800 GCDDemo01[1945:55274] 1 out
2019-01-15 14:58:55.266507+0800 GCDDemo01[1945:55274] 2 in
2019-01-15 14:58:57.267504+0800 GCDDemo01[1945:55274] 2 out
2019-01-15 14:58:57.267880+0800 GCDDemo01[1945:55274] 3 in
2019-01-15 14:58:58.273349+0800 GCDDemo01[1945:55274] 3 out
Serial DisPatch Queue是一個(gè)串行隊(duì)列,只能同時(shí)執(zhí)行1個(gè)追加處理(即任務(wù)),當(dāng)用Dispatch_queue_create函數(shù)生成多個(gè)Serial DisPatch Queue時(shí),每個(gè)Serial DisPatch Queue均獲得一個(gè)線(xiàn)程,即多個(gè)Serial DisPatch Queue可并發(fā)執(zhí)行,同時(shí)處理添加到各個(gè)Serial DisPatch Queue中的任務(wù)
但要注意如果過(guò)多地使用多線(xiàn)程,就會(huì)消耗大量?jī)?nèi)存,引起大量的上下文切換,大幅度降低系統(tǒng)的響應(yīng)性能,所以我們只在為了避免多個(gè)線(xiàn)程更新相同資源導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)時(shí),使用Serial DisPatch Queue。
dispatch_after
- (void)dispatchAfter
{
NSLog(@"開(kāi)始調(diào)用");
//DISPATCH_TIME_NOW: 表示現(xiàn)在的時(shí)間
//ull:是C語(yǔ)言的數(shù)值字面量,是顯式表示類(lèi)型時(shí)使用的字符串(表示“unsigned long long”)
//NSEC_PER_MSEC: 表示可以以毫秒為單位計(jì)算
//
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
//在3s后用dispatch_async 函數(shù)追加block到main dispatch Queue
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"延遲調(diào)用");
});
//dispatch_walltime 函數(shù)用于計(jì)算絕對(duì)時(shí)間
//dispatch_time 函數(shù)用于計(jì)算相對(duì)時(shí)間
//dispatch_walltime(<#const struct timespec * _Nullable when#>, <#int64_t delta#>)
}
dispatch_Group
在開(kāi)發(fā)過(guò)程中,在追加到dispathc Queue 中的多個(gè)處理全部結(jié)束后想執(zhí)行結(jié)束處理,這種情況經(jīng)常出現(xiàn)。那么可以引入dispatch_Group
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
//與dispatch_asyn 不同的是指定生成的dispatch_group_t 為第一參數(shù)。指定的block屬于指定的group
dispatch_group_async(group, queue, ^{
NSLog(@"組一 %@",[NSThread currentThread] );
});
dispatch_group_async(group, queue, ^{
NSLog(@"組二 %@",[NSThread currentThread] );
});
dispatch_group_async(group, queue, ^{
NSLog(@"組三 %@",[NSThread currentThread] );
sleep(10);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"執(zhí)行完畢 %@",[NSThread currentThread] );
});
//第二參數(shù):指定為等待的時(shí)間(超時(shí))
//DISPATCH_TIME_FOREVER 永久等待,只要屬于group 的處理尚未執(zhí)行介紹,就會(huì)一直等待,中途不能取消
//DISPATCH_TIME_NOW 不等待即可判斷屬于group 的處理是否執(zhí)行結(jié)束
//long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
if (result == 0) {
NSLog(@"屬于group 的全部處理執(zhí)行結(jié)束");
} else {
NSLog(@"屬于group 的某一個(gè)處理還在執(zhí)行");
}
NSLog(@"主線(xiàn)程----%@",[NSThread mainThread]);
打印數(shù)據(jù)如下:
2019-01-15 15:32:28.715196+0800 GCDDemo01[2208:65352] 組三 <NSThread: 0x6000032872c0>{number = 5, name = (null)}
2019-01-15 15:32:28.715196+0800 GCDDemo01[2208:65354] 組二 <NSThread: 0x600003287200>{number = 4, name = (null)}
2019-01-15 15:32:28.715196+0800 GCDDemo01[2208:65353] 組一 <NSThread: 0x6000032bce40>{number = 3, name = (null)}
2019-01-15 15:32:38.716752+0800 GCDDemo01[2208:65320] 屬于group 的全部處理執(zhí)行結(jié)束
2019-01-15 15:32:38.716945+0800 GCDDemo01[2208:65320] 主線(xiàn)程----<NSThread: 0x6000032d9400>{number = 1, name = main}
2019-01-15 15:32:38.747027+0800 GCDDemo01[2208:65320] 執(zhí)行完畢 <NSThread: 0x6000032d9400>{number = 1, name = main}
那么這個(gè)能夠解決在開(kāi)發(fā)過(guò)程中,一個(gè)控制器里面,有2個(gè)異步網(wǎng)絡(luò)請(qǐng)求,當(dāng)所有的請(qǐng)求結(jié)束后刷新頁(yè)面呢?
如下:
- (void)dispatchGroupDemo
{
NSURLSession *session = [NSURLSession sharedSession];
dispatch_queue_t dispatchQueue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"got data from internet1");
}];
[task resume];
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"got data from internet2");
}];
[task resume];
});
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"end");
});
/**
上述輸出結(jié)果:顯然沒(méi)有的達(dá)到要求
這是因?yàn)檫@里的網(wǎng)絡(luò)請(qǐng)求是個(gè)異步的方法,沒(méi)有等待具體的數(shù)據(jù)返回,放入的dispatch queue的 block就執(zhí)行完畢了。所以沒(méi)收到2個(gè)網(wǎng)絡(luò)數(shù)據(jù),就提前調(diào)用了dispatch_group_notify指定的結(jié)束方法。
2018-08-21 17:36:45.729321+0800 GCDDemo01[97318:3993362] end
2018-08-21 17:36:45.832798+0800 GCDDemo01[97318:3993495] got data from internet2
2018-08-21 17:36:45.870109+0800 GCDDemo01[97318:3993455] got data from internet1
*/
}
很明顯,這個(gè)方法行不通, 這是因?yàn)檫@里的網(wǎng)絡(luò)請(qǐng)求是個(gè)異步的方法,沒(méi)有等待具體的數(shù)據(jù)返回,放入的dispatch queue的 block就執(zhí)行完畢了。所以沒(méi)收到2個(gè)網(wǎng)絡(luò)數(shù)據(jù),就提前調(diào)用了dispatch_group_notify指定的結(jié)束方法。
下面這個(gè)勉強(qiáng)能行
- (void)dispatchGroupDemo02
{
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_t dispatchGroup = dispatch_group_create();
//手動(dòng)指示一個(gè)block塊已進(jìn)入組
//調(diào)用此函數(shù)表示另一個(gè)block塊已通過(guò)除 dispatch_group_async() 之外的其他方法加入該組。對(duì)這個(gè)函數(shù)的調(diào)用必須與dispatch_group_leave()進(jìn)行平衡。
dispatch_group_enter(dispatchGroup);
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"got data from internet1");
dispatch_group_leave(dispatchGroup);
}];
[task resume];
dispatch_group_enter(dispatchGroup);
NSURLSessionDataTask *task2 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"got data from internet2");
dispatch_group_leave(dispatchGroup);
}];
[task2 resume];
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"end");
});
/**
輸出結(jié)果:
2018-08-21 17:41:02.463886+0800 GCDDemo01[97790:4012417] got data from internet1
2018-08-21 17:41:02.493294+0800 GCDDemo01[97790:4012421] got data from internet2
2018-08-21 17:41:02.493488+0800 GCDDemo01[97790:4012365] end
相對(duì)于簡(jiǎn)單的dispatch_group_async,dispatch_group_enter 和 dispatch_group_leave 可以對(duì)group進(jìn)行更細(xì)致的處理。
dispatch_group_enter:(上面已有解釋)
簡(jiǎn)單的說(shuō),就是dispatch_group_enter會(huì)對(duì)group的內(nèi)部計(jì)數(shù)加一,dispatch_group_leave會(huì)對(duì)group的內(nèi)部計(jì)數(shù)減一,就類(lèi)似以前的retain和release方法。說(shuō)白了也是維護(hù)了一個(gè)計(jì)數(shù)器。
以前我的做法就是自己維護(hù)計(jì)數(shù)器。在發(fā)送網(wǎng)絡(luò)請(qǐng)求前,記下發(fā)送總數(shù),數(shù)據(jù)返回后,在同一個(gè)thread中(或者在一個(gè)DISPATCH_QUEUE_SERIAL類(lèi)型的dispatch_queue中),對(duì)計(jì)數(shù)器進(jìn)行+1操作,當(dāng)計(jì)數(shù)器和網(wǎng)絡(luò)請(qǐng)求數(shù)相等時(shí),調(diào)用最后的處理。
相比自己的處理的計(jì)數(shù)器,dispatch_group_enter 處理方法可能顯得更正規(guī)一些,代碼更規(guī)范了,但執(zhí)行效果是一樣的。。。
今天再改其他的工程的時(shí)候,又遇到了這個(gè)問(wèn)題,有一個(gè)值,需要2個(gè)異步操作查詢(xún)回2個(gè)值進(jìn)行計(jì)算,因此必須再2個(gè)異步操作結(jié)束后才能進(jìn)行計(jì)算操作。開(kāi)始試著使用了OperationQueue,想用addDependency方法,但是這個(gè)方法無(wú)法靈活地控制,只適合block內(nèi)容已經(jīng)確定的情況。對(duì)于我遇到的這種異步操作,block的內(nèi)容是不定的,需要依賴(lài)異步的返回,用operation queue會(huì)遇到各種問(wèn)題,無(wú)法解決問(wèn)題,十分復(fù)雜!
*/
}
dispatch_barrier_async
在訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)或文件時(shí),使用同步隊(duì)列Serial dispatch queue 可避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,
寫(xiě)入處理確實(shí)不可與其他寫(xiě)入處理以及包含讀取處理的其他某些處理并行執(zhí)行。但是如果讀取處理只是與讀取粗糲并行執(zhí)行,這是不會(huì)有問(wèn)題的;
為了高校的訪(fǎng)問(wèn),讀取處理追加到 并行隊(duì)列Concurrent dispatch queue 中,寫(xiě)入處理在任意一個(gè)讀取處理沒(méi)有執(zhí)行的狀態(tài)下,追加到Serial dispatch queue 中即可(寫(xiě)入處理之前,讀取處理不可執(zhí)行)
為了解決這個(gè)問(wèn)題,引入:dispatch_barrier_async
dispatch_queue_t queue = dispatch_queue_create("label1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"異步一 %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"異步二 %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"異步三 %@",[NSThread currentThread]);
});
//dispatch_barrier_async函數(shù):
//會(huì)等待追加到 并行隊(duì)列queue 上的并行執(zhí)行的處理全部結(jié)束以后,再將指定的處理追加到該 并行隊(duì)列queue 中,
//然后再由dispatch_barrier_async函數(shù)追加的處理執(zhí)行完畢后, 并行隊(duì)列queue 才恢復(fù)為一般的動(dòng)作
//簡(jiǎn)單地說(shuō),就是在這個(gè)函數(shù)之前被提交到quque里的block一定會(huì)被先執(zhí)行,之后執(zhí)行dispatch_barrier_async設(shè)定的block,最后執(zhí)行調(diào)用dispatch_barrier_async之后才提交到queue里的block。
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
});
dispatch_async(queue, ^{
NSLog(@"異步四 %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"異步五 %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"異步六 %@",[NSThread currentThread]);
});
打印如下
2019-01-15 16:00:16.373259+0800 GCDDemo01[2440:75032] 異步一 <NSThread: 0x600003f4edc0>{number = 3, name = (null)}
2019-01-15 16:00:16.373259+0800 GCDDemo01[2440:75031] 異步三 <NSThread: 0x600003f70f40>{number = 5, name = (null)}
2019-01-15 16:00:16.373261+0800 GCDDemo01[2440:75033] 異步二 <NSThread: 0x600003f4f140>{number = 4, name = (null)}
2019-01-15 16:00:16.373659+0800 GCDDemo01[2440:75032] dispatch_barrier_async
2019-01-15 16:00:16.373819+0800 GCDDemo01[2440:75031] 異步五 <NSThread: 0x600003f70f40>{number = 5, name = (null)}
2019-01-15 16:00:16.373840+0800 GCDDemo01[2440:75032] 異步四 <NSThread: 0x600003f4edc0>{number = 3, name = (null)}
2019-01-15 16:00:16.373819+0800 GCDDemo01[2440:75033] 異步六 <NSThread: 0x600003f4f140>{number = 4, name = (null)}
dispatch_barrier_async函數(shù):
1)會(huì)等待追加到 并行隊(duì)列queue 上的并行執(zhí)行的處理全部結(jié)束以后,再將指定的處理追加到該 并行隊(duì)列queue 中,
2)然后再由dispatch_barrier_async函數(shù)追加的處理執(zhí)行完畢后, 并行隊(duì)列queue 才恢復(fù)為一般的動(dòng)作
3)簡(jiǎn)單地說(shuō),就是在這個(gè)函數(shù)之前被提交到quque里的block一定會(huì)被先執(zhí)行,之后執(zhí)行dispatch_barrier_async設(shè)定的block,最后執(zhí)行調(diào)用dispatch_barrier_async之后才提交到queue里的block。
dispatch_sync
同步:
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//一旦調(diào)用dispatch_sync函數(shù),那么指定的處理執(zhí)行結(jié)束之前,該函數(shù)不會(huì)返回(返回的意思就是從隊(duì)列你們出來(lái))
dispatch_sync(globalQueue, ^{
NSLog(@"同步操作 并行隊(duì)列1 %@",[NSThread currentThread]);
});
dispatch_sync(globalQueue, ^{
NSLog(@"同步操作 并行隊(duì)列2 %@",[NSThread currentThread]);
});
dispatch_sync(globalQueue, ^{
NSLog(@"同步操作 并行隊(duì)列3 %@",[NSThread currentThread]);
});
dispatch_sync(globalQueue, ^{
NSLog(@"同步操作 并行隊(duì)列4 %@",[NSThread currentThread]);
});
NSLog(@"同步操作 并行隊(duì)列5 %@",[NSThread currentThread]);
打?。?都在主線(xiàn)程
2019-01-15 16:19:55.460438+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列1 <NSThread: 0x600000266e80>{number = 1, name = main}
2019-01-15 16:19:55.460683+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列2 <NSThread: 0x600000266e80>{number = 1, name = main}
2019-01-15 16:19:55.460814+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列3 <NSThread: 0x600000266e80>{number = 1, name = main}
2019-01-15 16:19:55.460953+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列4 <NSThread: 0x600000266e80>{number = 1, name = main}
2019-01-15 16:19:55.461079+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列5 <NSThread: 0x600000266e80>{number = 1, name = main}
為什么都是在主線(xiàn)程里面執(zhí)行?
這是一個(gè) 同步函數(shù)+并發(fā)隊(duì)列
這些任務(wù)都是創(chuàng)建一個(gè)就立馬執(zhí)行,執(zhí)行完才創(chuàng)建下一個(gè),因?yàn)槭峭胶瘮?shù),所以不新建線(xiàn)程,只要是同步函數(shù),就不會(huì)新建線(xiàn)程。
并發(fā)隊(duì)列與否,并不影響同步函數(shù)的創(chuàng)建,因?yàn)楸旧砭筒荒芏鄤?chuàng)建線(xiàn)程,也就不存在并發(fā)
容易造成死鎖
//同步函數(shù)+串隊(duì)列
NSLog(@"死鎖問(wèn)題輸出01 %@",[NSThread currentThread]);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{ //崩潰到這一步
NSLog(@"死鎖問(wèn)題輸出02 %@",[NSThread currentThread]);
});
NSLog(@"死鎖問(wèn)題輸出03 %@",[NSThread currentThread]);
原因:(查看很多博客,五花八門(mén),解釋的很多不一樣,但總的一點(diǎn)就是互相阻塞)
下面是我總結(jié)的一個(gè)說(shuō)法:
總結(jié):
一旦調(diào)用dispatch_sync函數(shù),那么指定的處理執(zhí)行結(jié)束之前,該函數(shù)不會(huì)返回(返回的意思就是從隊(duì)列你們出來(lái))
相反dispatch_async這個(gè)函數(shù)是開(kāi)一條線(xiàn)程出來(lái)執(zhí)行Block,不等Block返回就直接返回了。
我們通常把任務(wù)也就是所說(shuō)的block塊(swift 叫閉包)交給GCD函數(shù),函數(shù)把任務(wù)添加到隊(duì)列queue(先進(jìn)先出的原則)里面
隊(duì)列遵循嚴(yán)格的先進(jìn)后出的原則,同一個(gè)queue中,最早入列的block,會(huì)最早的分配給線(xiàn)程去執(zhí)行
GCD隊(duì)列只是組織待執(zhí)行任務(wù)的一個(gè)數(shù)據(jù)結(jié)構(gòu)封裝,而線(xiàn)程才是執(zhí)行任務(wù)的人
串行隊(duì)列:隊(duì)列中的任務(wù)是一個(gè)執(zhí)行完成后才去執(zhí)行另一個(gè)
解釋?zhuān)? 1)主線(xiàn)程調(diào)用dispatch_sync這個(gè)函數(shù) 【這一步把dispatch_sync 函數(shù)本身 進(jìn)入main隊(duì)列,記做A任務(wù)】
2)當(dāng)這個(gè)函數(shù)返回(執(zhí)行完畢)的時(shí)候主線(xiàn)程才能往下執(zhí)行 【dispatch_sync 函數(shù)執(zhí)行完畢才能出main隊(duì)列,才能繼續(xù)往下執(zhí)行,執(zhí)行B任務(wù),也就是block塊】
3)但dispatch_sync返回的條件是里面的Block返回,里面的Block是不會(huì)執(zhí)行的,因?yàn)樗潜徊宓街麝?duì)列最后執(zhí)行,然而因?yàn)閐ispatch_sync無(wú)法返回,所以主隊(duì)列無(wú)法執(zhí)行到最后一個(gè)任務(wù)。 【執(zhí)行block任務(wù),記做B任務(wù),它在任務(wù)A后面,必須等待任務(wù)A執(zhí)行完畢,才能執(zhí)行B】
【A任務(wù) 等待B任務(wù)執(zhí)行結(jié)束后才能出main隊(duì)列,但是串行main隊(duì)列中(同步函數(shù)),B任務(wù)在A任務(wù)后面,A任務(wù)不完成,是不會(huì)執(zhí)行B任務(wù)的,從而造成互相阻塞,從而死鎖】
如果隊(duì)列 改為 自行創(chuàng)建的串行隊(duì)列 dispatch_queue_create("d", NULL),不會(huì)造成死鎖,為什么?
因?yàn)椋?block塊添加的隊(duì)列 是自行創(chuàng)建的隊(duì)列,跟dispatch_sync函數(shù)添加到的mian隊(duì)列不是同一個(gè),blocl塊添加到隊(duì)列里,是隊(duì)列的頭也是尾,所以先執(zhí)行并返回,然后dispatch_sync返回,繼續(xù)執(zhí)行dispatch_sync函數(shù)下面的語(yǔ)句
圖解:

dispatch_apply
- dispatch_apply 函數(shù)時(shí)dispatch_sync 函數(shù)和dispatch Groupd的關(guān)聯(lián)API
- dispatch_apply 函數(shù)按指定的次數(shù)將指定的block 追加到指定的dispatch_queue_t 中,并等待全部處理執(zhí)行結(jié)束
//參數(shù)一: 重復(fù)次數(shù)
//參數(shù)二:追加對(duì)象的queue
//參數(shù)三:追加的處理
dispatch_apply(10, global, ^(size_t index) {
NSLog(@"%zu",index);
//sleep(2);
});
NSLog(@"done");
輸出
2019-01-15 17:05:15.313947+0800 GCDDemo01[2990:98143] 1
2019-01-15 17:05:15.313947+0800 GCDDemo01[2990:98097] 0
2019-01-15 17:05:15.313949+0800 GCDDemo01[2990:98134] 2
2019-01-15 17:05:15.313977+0800 GCDDemo01[2990:98137] 3
2019-01-15 17:05:15.314154+0800 GCDDemo01[2990:98097] 7
2019-01-15 17:05:15.314154+0800 GCDDemo01[2990:98143] 6
2019-01-15 17:05:15.314155+0800 GCDDemo01[2990:98134] 5
2019-01-15 17:05:15.314155+0800 GCDDemo01[2990:98137] 4
2019-01-15 17:05:15.314260+0800 GCDDemo01[2990:98143] 8
2019-01-15 17:05:15.314268+0800 GCDDemo01[2990:98097] 9
2019-01-15 17:05:15.314756+0800 GCDDemo01[2990:98097] done
因?yàn)槿株?duì)列,所以輸出結(jié)果 不確定,但最后 done 輸出必定在最后的位置
這是因?yàn)?code>dispatch_apply函數(shù)會(huì)等待全部處理執(zhí)行結(jié)束
- 在dispatch_async函數(shù)中異步執(zhí)行dispatch_apply函數(shù),模擬dispatch_sync的同步效果
- (void)dispatch_apply03
{
//在dispatch_async函數(shù)中異步執(zhí)行dispatch_apply函數(shù),模擬dispatch_sync的同步效果
NSArray *array = @[@"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", @"j"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"執(zhí)行一");
//等待dispatch_apply 函數(shù)中的全部處理執(zhí)行結(jié)束
dispatch_apply([array count], queue, ^(size_t index) {
//處理包含在NArrray對(duì)象的全部對(duì)象
NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
});
NSLog(@"執(zhí)行二");
//在main dispatch queue 中非同步執(zhí)行
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主線(xiàn)程執(zhí)行用戶(hù)界面更新等操作");
});
});
}
2019-01-15 17:10:11.744106+0800 GCDDemo01[3034:99956] 執(zhí)行一
2019-01-15 17:10:11.744316+0800 GCDDemo01[3034:99956] 0: a
2019-01-15 17:10:11.744419+0800 GCDDemo01[3034:99956] 1: b
2019-01-15 17:10:11.744516+0800 GCDDemo01[3034:99956] 2: c
2019-01-15 17:10:11.744601+0800 GCDDemo01[3034:99956] 3: d
2019-01-15 17:10:11.744695+0800 GCDDemo01[3034:99956] 4: e
2019-01-15 17:10:11.744778+0800 GCDDemo01[3034:99956] 5: f
2019-01-15 17:10:11.744881+0800 GCDDemo01[3034:99956] 6: g
2019-01-15 17:10:11.745772+0800 GCDDemo01[3034:99956] 7: h
2019-01-15 17:10:11.746091+0800 GCDDemo01[3034:99956] 8: i
2019-01-15 17:10:11.746418+0800 GCDDemo01[3034:99956] 9: j
2019-01-15 17:10:11.746750+0800 GCDDemo01[3034:99956] 執(zhí)行二
2019-01-15 17:10:11.759049+0800 GCDDemo01[3034:99913] 回到主線(xiàn)程執(zhí)行用戶(hù)界面更新等操作
dispatch_suspend
有時(shí)候希望不執(zhí)行已追加的處理 從而引入dispatch_suspend
dispatch_suspend函數(shù): 掛起指定的dispatch_queue
dispatch_resume函數(shù): 恢復(fù)指定的dispatch_queue
注意: 對(duì)已經(jīng)執(zhí)行的處理沒(méi)有影響,掛起后,追加到dispatch_queue 中尚未執(zhí)行處理的,在此之后停止執(zhí)行。而恢復(fù)則使得這些處理能夠繼續(xù)執(zhí)行
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
//提交第一個(gè)block,延時(shí)5秒打印。
dispatch_async(queue, ^{
sleep(5);
NSLog(@"After 5 seconds...");
});
//提交第二個(gè)block,也是延時(shí)5秒打印
dispatch_async(queue, ^{
sleep(5);
NSLog(@"After 5 seconds again...");
});
//延時(shí)一秒
NSLog(@"sleep 1 second...");
sleep(1);
//掛起隊(duì)列
NSLog(@"suspend...");
dispatch_suspend(queue);
//延時(shí)10秒
NSLog(@"sleep 10 second...");
sleep(10);
//恢復(fù)隊(duì)列
NSLog(@"resume...");
dispatch_resume(queue);
打印如下:
2019-01-15 17:16:51.256678+0800 GCDDemo01[3094:102562] sleep 1 second...
2019-01-15 17:16:52.257143+0800 GCDDemo01[3094:102562] suspend...
2019-01-15 17:16:52.257403+0800 GCDDemo01[3094:102562] sleep 10 second...
2019-01-15 17:16:56.260057+0800 GCDDemo01[3094:102606] After 5 seconds...
2019-01-15 17:17:02.258408+0800 GCDDemo01[3094:102562] resume...
2019-01-15 17:17:07.262380+0800 GCDDemo01[3094:102603] After 5 seconds again...
dispatch_semaphore_t 信號(hào)量
dispatch_semaphore_t 是持有計(jì)數(shù)的信號(hào),該計(jì)數(shù)是多線(xiàn)程編程中的計(jì)數(shù)類(lèi)型信號(hào)。
- 創(chuàng)建信號(hào)
//參數(shù)表示計(jì)數(shù)的初始值
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
- 等待信號(hào)量
【當(dāng)計(jì)數(shù)值>=1,或者在待機(jī)中計(jì)數(shù)值>= 1,對(duì)該計(jì)數(shù)進(jìn)行減法并從dispatch_semaphore_wait函數(shù)返回; 返回值==0時(shí),可以安全的執(zhí)行需要進(jìn)行怕他控制的處理】
//long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//時(shí)間
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
- dispatch_semaphore_signal 提高信號(hào)量
dispatch_semaphore_signal(semaphore);
//計(jì)數(shù)值 ==1
//保證可訪(fǎng)問(wèn)數(shù)組NSMutableArray類(lèi)對(duì)象的線(xiàn)程只能有一個(gè)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray *marray = [NSMutableArray new];
for (int i = 0; i < 100000; i ++) {
dispatch_async(global, ^{
//等待dispatch_semaphore,
//一直等待,知道dispatch_semaphore 技術(shù) >= 1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//由于dispatch_semaphore 技術(shù)>= 1,所以將dispatch_semaphore 計(jì)數(shù)值-1
//dispatch_semaphore_wait 函數(shù)執(zhí)行返回,到此 計(jì)數(shù)為0
//可以安全w訪(fǎng)問(wèn)
[marray addObject:[NSNumber numberWithInteger:i]];
//排他控制處理結(jié)束
//所以通過(guò)dispatch_semaphore_signal 函數(shù) 將dispatch_semaphore 計(jì)數(shù) +1
dispatch_semaphore_signal(semaphore);
});
}
- 我們要下載很多圖片,并發(fā)異步進(jìn)行,每個(gè)下載都會(huì)開(kāi)辟一個(gè)新線(xiàn)程,可是我們又擔(dān)心太多線(xiàn)程肯定cpu吃不消,那么我們這里也可以用信號(hào)量控制一下最大開(kāi)辟線(xiàn)程數(shù)。
-(void)dispatchSignal{
//crate的value表示,最多幾個(gè)資源可訪(fǎng)問(wèn)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務(wù)1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
//任務(wù)2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
//任務(wù)3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
}
打印如下:
2019-01-15 18:52:27.165995+0800 GCDDemo01[3896:134773] run task 1
2019-01-15 18:52:27.166000+0800 GCDDemo01[3896:134771] run task 3
2019-01-15 18:52:28.171006+0800 GCDDemo01[3896:134773] complete task 1
2019-01-15 18:52:28.171014+0800 GCDDemo01[3896:134771] complete task 3
2019-01-15 18:52:28.171393+0800 GCDDemo01[3896:134774] run task 2
2019-01-15 18:52:29.176322+0800 GCDDemo01[3896:134774] complete task 2
總結(jié):由于設(shè)定的信號(hào)值為2,先執(zhí)行兩個(gè)線(xiàn)程,等執(zhí)行完一個(gè),才會(huì)繼續(xù)執(zhí)行下一個(gè),保證同一時(shí)間執(zhí)行的線(xiàn)程數(shù)不超過(guò)2。
- 當(dāng) 創(chuàng)建信號(hào)量=1時(shí) dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
輸出:
2019-01-15 18:54:22.922276+0800 GCDDemo01[3935:136035] run task 1
2019-01-15 18:54:23.925043+0800 GCDDemo01[3935:136035] complete task 1
2019-01-15 18:54:23.925346+0800 GCDDemo01[3935:136038] run task 2
2019-01-15 18:54:24.927118+0800 GCDDemo01[3935:136038] complete task 2
2019-01-15 18:54:24.927407+0800 GCDDemo01[3935:136036] run task 3
2019-01-15 18:54:25.932808+0800 GCDDemo01[3935:136036] complete task 3
總結(jié):由于設(shè)定的信號(hào)值為1,先執(zhí)行1個(gè)線(xiàn)程,等執(zhí)行完一個(gè),才會(huì)繼續(xù)執(zhí)行下一個(gè),保證同一時(shí)間執(zhí)行的線(xiàn)程數(shù)不超過(guò)1
對(duì)比下面兩個(gè)方法的差異:
- (NSString *)sting
{
__block NSString *s = nil;
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(globalDispatchQueue, ^{
s = @"123";
});
return s;
}
- (NSString *)sting_dispatch_semaphore_t
{
__block NSString *s = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(globalDispatchQueue, ^{
s = @"123";
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return s;
}
技術(shù)信號(hào)的形式,等待異步執(zhí)行的結(jié)果,并將結(jié)果返回.
信號(hào)量:就是一種可用來(lái)控制訪(fǎng)問(wèn)資源的數(shù)量的標(biāo)識(shí),設(shè)定了一個(gè)信號(hào)量,在線(xiàn)程訪(fǎng)問(wèn)之前,加上信號(hào)量的處理,則可告知系統(tǒng)按照我們指定的信號(hào)量數(shù)量來(lái)執(zhí)行多個(gè)線(xiàn)程。
dispatch_once_t 一般創(chuàng)建單例
- 保證應(yīng)用程序只能執(zhí)行一次制定處理的api
- (void)dispatchOnce
{
// 保證程序只執(zhí)行一次,一般用于生成單例模式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
}
Dispatch I/O
- 提高讀取速率,嘗試使用
Dispatch I/O
GCD實(shí)現(xiàn)
-
Dispatch Queue
通常,應(yīng)用程序中編寫(xiě)的線(xiàn)程管理用的代碼要在系統(tǒng)級(jí)實(shí)現(xiàn)
- Dispatch Source