GCD简介
- GCD全称Grand Central Dispatch,可译为“牛逼的中枢调度系统”,是苹果公司为多核的并行运算提供的解决方案。
- 开发者借助GCD无需直接操作线程,只需要将准备好的和要执行的任务添加到Dispatch Queue(队列)中,GCD会根据队列类型(串行&并发)和任务的执行类型(同步&异步)来确定要不要开启子线程、和任务的执行顺序。
- 任务的执行顺序遵循队列的FIFO原则,先进先出、后进后出。
- 并且要不要开启线程、开几条线程以及线程的生命周期(创建线程、调度热舞、销毁线程)都不需要开发者关心。
- GCD会自动利用更多的CPU内核(比如:双核、四核、乃至八核),本人也相信苹果的GCD技术也是一大伏笔,虽然目前市场上的苹果手机大都是双核,相信在不久的未来苹果推出更多核芯CPU时GCD会自动适配,届时所有程序员会发现,苹果已经早先一步有了应对多核硬件的技术,并且也不用担心硬件的革新带来项目重构的问题,所以放心地使用GCD!
GCD的几个概念
- 任务 同步&异步:
/**同步执行*/
dispatch_sync(dispatch_queue_t _Nonnull queue, ^(void)block)
/**异步步执行*/
dispatch_async(dispatch_queue_t _Nonnull queue, ^(void)block)
这里只要执行的代码,为Block代码块。既然是Block,是不是烦恼的循环引用又来了,block只是一个局部变量,执行完毕之后就释放掉了,不用担心循环引用问题。任务的执行又分同步和异步。
同步:当前任务(代码)没有执行完毕不会执行下一个任务;
异步:当前任务没有执行完毕同样会执行下一个任务(只要有任务,GCD就回到线程池中取线程)(主队列除外)
- 队列 串行&并发:
/**
参数:
1.线程名称
2.DISPATCH_QUEUE_SERIAL == NULL 串行
DISPATCH_QUEUE_CONCURRENT 并发
*/
dispatch_queue_create(const char * _Nullable label, dispatch_queue_attr_t _Nullable attr)
主要负责调度任务,所有队列都遵循FIFO原则。队列又分为串行队列和并发队列。
串行 队列:一个一个地调度任务。
并发 队列:可以同时调度多个任务,开不开线程是有任务决定的。如果是同步任务:当一个任务没有执行完成,队列也会取任务,只是取出的任务要等待前一个任务执行完毕才开始执行,不会开启线程;如果是异步任务:GCD会开启线程同时执行多个任务。
- 小结:
开不开线程取决于任务,同步不开线程,异步开线程;
开几条线程由队列决定,串行只开一条,并发(只有异步条件下)可以开启多条。
GCD的相关用法
- 串行队列,同步任务
//串行
dispatch_queue_t q = dispatch_queue_create("chenjinguo", NULL);
for (int i = 0; i < 10; i ++) {
//同步
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
打印
2018-04-03 11:53:46.883277+0800 GCD演示[1099:402251] {number = 1, name = main} 0
2018-04-03 11:53:46.883576+0800 GCD演示[1099:402251] {number = 1, name = main} 1
2018-04-03 11:53:46.883763+0800 GCD演示[1099:402251] {number = 1, name = main} 2
2018-04-03 11:53:46.883928+0800 GCD演示[1099:402251] {number = 1, name = main} 3
2018-04-03 11:53:46.884098+0800 GCD演示[1099:402251] {number = 1, name = main} 4
2018-04-03 11:53:46.884260+0800 GCD演示[1099:402251] {number = 1, name = main} 5
2018-04-03 11:53:46.884390+0800 GCD演示[1099:402251] {number = 1, name = main} 6
2018-04-03 11:53:46.884529+0800 GCD演示[1099:402251] {number = 1, name = main} 7
2018-04-03 11:53:46.884648+0800 GCD演示[1099:402251] {number = 1, name = main} 8
2018-04-03 11:53:46.884789+0800 GCD演示[1099:402251] {number = 1, name = main} 9
- 不会开启线程,顺序执行
- 串行队列,同步任务
//串行
dispatch_queue_t q = dispatch_queue_create("chen_jinguo",DISPATCH_QUEUE_CONCURRENT);
for(int i = 0; i < 10; i ++){
NSLog(@"---------%d-------",i);
//异步
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
打印
2018-04-03 11:58:17.697224+0800 GCD演示[1126:425688] ---------0-------
2018-04-03 11:58:17.697617+0800 GCD演示[1126:426370] {number = 3, name = (null)} 0
2018-04-03 11:58:17.697436+0800 GCD演示[1126:425688] ---------1-------
2018-04-03 11:58:17.698929+0800 GCD演示[1126:425688] ---------2-------
2018-04-03 11:58:17.699189+0800 GCD演示[1126:425688] ---------3-------
2018-04-03 11:58:17.699223+0800 GCD演示[1126:426370] {number = 3, name = (null)} 1
2018-04-03 11:58:17.699405+0800 GCD演示[1126:425688] ---------4-------
2018-04-03 11:58:17.699447+0800 GCD演示[1126:426370] {number = 3, name = (null)} 2
2018-04-03 11:58:17.701129+0800 GCD演示[1126:425688] ---------5-------
2018-04-03 11:58:17.702702+0800 GCD演示[1126:426370] {number = 3, name = (null)} 3
2018-04-03 11:58:17.703452+0800 GCD演示[1126:425688] ---------6-------
2018-04-03 11:58:17.703831+0800 GCD演示[1126:426370] {number = 3, name = (null)} 4
2018-04-03 11:58:17.704094+0800 GCD演示[1126:425688] ---------7-------
2018-04-03 11:58:17.704316+0800 GCD演示[1126:426370] {number = 3, name = (null)} 5
2018-04-03 11:58:17.743690+0800 GCD演示[1126:425688] ---------8-------
2018-04-03 11:58:17.743782+0800 GCD演示[1126:426370] {number = 3, name = (null)} 6
2018-04-03 11:58:17.743979+0800 GCD演示[1126:425688] ---------9-------
2018-04-03 11:58:17.744001+0800 GCD演示[1126:426370] {number = 3, name = (null)} 7
2018-04-03 11:58:17.744380+0800 GCD演示[1126:425688] come here
2018-04-03 11:58:17.744407+0800 GCD演示[1126:426370] {number = 3, name = (null)} 8
2018-04-03 11:58:17.744776+0800 GCD演示[1126:426370] {number = 3, name = (null)} 9
- 会开启一条线程,顺序执行
- 并发队列,异步任务
//队列 - 并发 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("chenjinguo", DISPATCH_QUEUE_CONCURRENT);
//执行 - 异步
for (int i = 0; i < 10; i ++) {
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
打印
2018-04-03 13:36:05.457846+0800 GCD演示[1213:499948] come here
2018-04-03 13:36:05.458007+0800 GCD演示[1213:500170] {number = 4, name = (null)} 1
2018-04-03 13:36:05.458008+0800 GCD演示[1213:500169] {number = 3, name = (null)} 0
2018-04-03 13:36:05.458010+0800 GCD演示[1213:500171] {number = 6, name = (null)} 3
2018-04-03 13:36:05.458012+0800 GCD演示[1213:500180] {number = 5, name = (null)} 2
2018-04-03 13:36:05.458301+0800 GCD演示[1213:500170] {number = 4, name = (null)} 5
2018-04-03 13:36:05.458310+0800 GCD演示[1213:500168] {number = 7, name = (null)} 4
2018-04-03 13:36:05.458323+0800 GCD演示[1213:500169] {number = 3, name = (null)} 6
2018-04-03 13:36:05.458345+0800 GCD演示[1213:500171] {number = 6, name = (null)} 7
2018-04-03 13:36:05.458451+0800 GCD演示[1213:500168] {number = 7, name = (null)} 8
2018-04-03 13:36:05.458476+0800 GCD演示[1213:500170] {number = 4, name = (null)} 9
- 会开启多条线程,非顺序执行
- 并发队列,同步任务
//队列 - 并发 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("chenjinguo", DISPATCH_QUEUE_CONCURRENT);
//执行 - 同步
for (int i = 0; i < 10; i ++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
打印
2018-04-03 13:37:58.162147+0800 GCD演示[1228:510822] {number = 1, name = main} 0
2018-04-03 13:37:58.162436+0800 GCD演示[1228:510822] {number = 1, name = main} 1
2018-04-03 13:37:58.163300+0800 GCD演示[1228:510822] {number = 1, name = main} 2
2018-04-03 13:37:58.163621+0800 GCD演示[1228:510822] {number = 1, name = main} 3
2018-04-03 13:37:58.163761+0800 GCD演示[1228:510822] {number = 1, name = main} 4
2018-04-03 13:37:58.163916+0800 GCD演示[1228:510822] {number = 1, name = main} 5
2018-04-03 13:37:58.164035+0800 GCD演示[1228:510822] {number = 1, name = main} 6
2018-04-03 13:37:58.164410+0800 GCD演示[1228:510822] {number = 1, name = main} 7
2018-04-03 13:37:58.164839+0800 GCD演示[1228:510822] {number = 1, name = main} 8
2018-04-03 13:37:58.165329+0800 GCD演示[1228:510822] {number = 1, name = main} 9
2018-04-03 13:37:58.165824+0800 GCD演示[1228:510822] come here
- 不会开启线程,顺序执行
- 同步任务
在开发中,通常会把耗时任务放在后台执行,有时候,有些任务彼此有“依赖”关系!
例子:登录、支付、下载
利用同步任务,能够任务的依赖关系,前一个是同步任务,如果不执行完,队列就不会调度后面的任务
dispatch_queue_t q = dispatch_queue_create("ChenJinguo", DISPATCH_QUEUE_CONCURRENT);
//1、登录
dispatch_sync(q, ^{
NSLog(@"用户登录----- %@",[NSThread currentThread]);
});
//2、支付
dispatch_sync(q, ^{
NSLog(@"用户支付----- %@",[NSThread currentThread]);
});
for (int i = 0; i < 10; i ++) {
NSLog(@"%d\n",i);
}
//3、下载
dispatch_async(q, ^{
NSLog(@"用户下载----- %@",[NSThread currentThread]);
});
NSLog(@"come here!");
打印
2018-04-03 13:43:05.538233+0800 GCD演示[1264:539460] 用户登录----- {number = 1, name = main}
2018-04-03 13:43:05.538480+0800 GCD演示[1264:539460] 用户支付----- {number = 1, name = main}
2018-04-03 13:43:05.538938+0800 GCD演示[1264:539460] 0
2018-04-03 13:43:05.539076+0800 GCD演示[1264:539460] 1
2018-04-03 13:43:05.539220+0800 GCD演示[1264:539460] 2
2018-04-03 13:43:05.539321+0800 GCD演示[1264:539460] 3
2018-04-03 13:43:05.539431+0800 GCD演示[1264:539460] 4
2018-04-03 13:43:05.539679+0800 GCD演示[1264:539460] 5
2018-04-03 13:43:05.540171+0800 GCD演示[1264:539460] 6
2018-04-03 13:43:05.540635+0800 GCD演示[1264:539460] 7
2018-04-03 13:43:05.541084+0800 GCD演示[1264:539460] 8
2018-04-03 13:43:05.541530+0800 GCD演示[1264:539460] 9
2018-04-03 13:43:05.541870+0800 GCD演示[1264:539460] come here!
2018-04-03 13:43:05.541957+0800 GCD演示[1264:540171] 用户下载----- {number = 3, name = (null)}
- 因为登录和支付都是同步任务,在执行完同步任务之后才会开启子线程完成其他异步任务
思考:如果登录、支付和下载都是耗时任务,为了增强用户体验,不想将他们放在UI线程中,怎么样完成这三个任务的同步呢?
方法:将三个任务作为一个异步任务,这样不管放在串行队列或者并发队列中,GCD都会开辟子线程调度此任务,然后在开启的子线程上同步调度三个任务,就完成了异步中同步调度任务
dispatch_queue_t q = dispatch_queue_create("chen", DISPATCH_QUEUE_CONCURRENT);
//包装三个任务为block 异步执行
void (^task)() = ^{
dispatch_sync(q, ^{
for (int i = 0; i < 10; i ++) {
NSLog(@"%d--- %@\n",i,[NSThread currentThread]);
}
});
//1、登录
dispatch_async(q, ^{
NSLog(@"用户登录----- %@",[NSThread currentThread]);
});
//2、支付
dispatch_async(q, ^{
NSLog(@"用户支付----- %@",[NSThread currentThread]);
});
//3、下载
dispatch_async(q, ^{
NSLog(@"用户下载----- %@",[NSThread currentThread]);
});
};
dispatch_async(q, task);
打印
2018-04-03 13:54:32.628778+0800 GCD演示[1297:579094] 0--- {number = 4, name = (null)}
2018-04-03 13:54:32.628785+0800 GCD演示[1297:579087] 用户登录----- {number = 3, name = (null)}
2018-04-03 13:54:32.629000+0800 GCD演示[1297:579094] 1--- {number = 4, name = (null)}
2018-04-03 13:54:32.629150+0800 GCD演示[1297:579087] 用户支付----- {number = 3, name = (null)}
2018-04-03 13:54:32.629169+0800 GCD演示[1297:579094] 2--- {number = 4, name = (null)}
2018-04-03 13:54:32.629267+0800 GCD演示[1297:579094] 3--- {number = 4, name = (null)}
2018-04-03 13:54:32.629269+0800 GCD演示[1297:579087] 用户下载----- {number = 3, name = (null)}
2018-04-03 13:54:32.629835+0800 GCD演示[1297:579094] 4--- {number = 4, name = (null)}
2018-04-03 13:54:32.630147+0800 GCD演示[1297:579094] 5--- {number = 4, name = (null)}
2018-04-03 13:54:32.630346+0800 GCD演示[1297:579094] 6--- {number = 4, name = (null)}
2018-04-03 13:54:32.630643+0800 GCD演示[1297:579094] 7--- {number = 4, name = (null)}
2018-04-03 13:54:32.631249+0800 GCD演示[1297:579094] 8--- {number = 4, name = (null)}
2018-04-03 13:54:32.631599+0800 GCD演示[1297:579094] 9--- {number = 4, name = (null)}
这样所有的任务都会在子线程中执行,并且三个任务之间有依赖关系
- 全局队列
GCD提供默认的并发队列供全局使用:全局队列。其获取方式和参数如下:
dispatch_get_global_queue(long identifier, unsigned long flags);
参数类型为:
long identifier:ios 8.0 告诉队列执行任务的“服务质量 quality of service”,系统提供的参数有:
QOS_CLASS_USER_INTERACTIVE 0x21, 用户交互(希望尽快完成,用户对结果很期望,不要放太耗时操作)
QOS_CLASS_USER_INITIATED 0x19, 用户期望(不要放太耗时操作)
QOS_CLASS_DEFAULT 0x15, 默认(不是给程序员使用的,用来重置对列使用的)
QOS_CLASS_UTILITY 0x11, 实用工具(耗时操作,可以使用这个选项)
QOS_CLASS_BACKGROUND 0x09, 后台
QOS_CLASS_UNSPECIFIED 0x00, 未指定
iOS 7.0 之前 优先级
DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
BACKGROUND表示用户不需要知道任务什么时候完成,如果选择这个选项速度慢得令人发指,非常不利于调试!对于优先级推荐不要搞得太负责,就用最简单,以免发生优先级反转。
unsigned long flags:苹果官方文档是这样解释的: Flags that are reserved for future use。标记是为了未来使用保留的!所以这个参数应该永远指定为0
注意:全局队列属于并发队列,建议在企业级应用开发过程中如果不使用全局队列,尽量给队列起名,这样有利于错误跟踪;另外,在MRC模式下,队列需要releasedispatch_release(q);//ARC 情况下不需要release!
- 延时执行
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
/**参数
1、when 时间
2、queue
3、Block
*/
//从现在开始执行多少纳秒之后,让queue调度Block的任务并且异步执行
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
dispatch_after(when, dispatch_queue_create("jinguo", NULL), ^{
NSLog(@"%@",[NSThread currentThread]);
});
- 执行一次
dispatch_once(dispatch_once_t * _Nonnull predicate, ^(void)block) ;
执行一次经常在单例中用到,是苹果提供的一次性机制,不仅能保证代码只执行一次,并且线程安全,线程安全表现在:dispatch_once_t * _Nonnull predicate
参数,其原理官方头文件也有表现:
_dispatch_once(dispatch_once_t *predicate,
DISPATCH_NOESCAPE dispatch_block_t block)
{
if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
dispatch_once(predicate, block);
} else {
dispatch_compiler_barrier();
}
DISPATCH_COMPILER_CAN_ASSUME(*predicate == ~0l);
}
其实具体是什么原理我也看不懂,毕竟C语言认识我我不认识它,其大概意思是我们外面定义的dispatch_once_t
参数在传入这个函数之后,使用它来保证线程安全的,其实我们做一次打印也可以看出个大概
for (int i = 0; i < 10; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
dispatch_once(&onceToken, ^{
NSLog(@" %@come here!",[NSThread currentThread]);
});
NSLog(@"来了 ----%d %@",i,[NSThread currentThread]);
});
}
打印
2018-04-03 14:17:22.679810+0800 GCD演示[1372:683676] 0
2018-04-03 14:17:22.679816+0800 GCD演示[1372:683678] 0
2018-04-03 14:17:22.679810+0800 GCD演示[1372:682541] 0
2018-04-03 14:17:22.679866+0800 GCD演示[1372:683679] 0
2018-04-03 14:17:22.680138+0800 GCD演示[1372:683678] {number = 3, name = (null)}come here!
2018-04-03 14:17:22.680170+0800 GCD演示[1372:683680] 5385
2018-04-03 14:17:22.680480+0800 GCD演示[1372:683681] 5385
2018-04-03 14:17:22.680785+0800 GCD演示[1372:683676] 来了 ----1 {number = 6, name = (null)}
2018-04-03 14:17:22.680785+0800 GCD演示[1372:682541] 来了 ----0 {number = 5, name = (null)}
2018-04-03 14:17:22.680795+0800 GCD演示[1372:683678] 来了 ----2 {number = 3, name = (null)}
2018-04-03 14:17:22.680805+0800 GCD演示[1372:683680] 来了 ----4 {number = 4, name = (null)}
2018-04-03 14:17:22.680813+0800 GCD演示[1372:683679] 来了 ----3 {number = 7, name = (null)}
2018-04-03 14:17:22.680992+0800 GCD演示[1372:683681] 来了 ----5 {number = 8, name = (null)}
2018-04-03 14:17:22.681220+0800 GCD演示[1372:683682] -1
2018-04-03 14:17:22.681490+0800 GCD演示[1372:683683] -1
2018-04-03 14:17:22.681692+0800 GCD演示[1372:683676] -1
2018-04-03 14:17:22.681766+0800 GCD演示[1372:683684] -1
2018-04-03 14:17:22.683852+0800 GCD演示[1372:683682] 来了 ----6 {number = 9, name = (null)}
2018-04-03 14:17:22.684087+0800 GCD演示[1372:683683] 来了 ----7 {number = 10, name = (null)}
2018-04-03 14:17:22.684468+0800 GCD演示[1372:683676] 来了 ----8 {number = 6, name = (null)}
2018-04-03 14:17:22.685031+0800 GCD演示[1372:683684] 来了 ----9 {number = 11, name = (null)}
onceToken的值一开始是0,执行到一次性语句时变为5385(或许是其他什么数),执行完毕变为-1,什么原理还望大神的指点。
还有一种一次性执行的方式:互斥锁 @synchronized()
,在单例中用到过,下面将两种单例创建的方式列举一下,并比较两种方式创建单例的效率如何:
@synchronized():
#import "Singleton.h"
static Singleton *_instance;
@implementation Singleton
+ (instancetype)share{
return [[self alloc]init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
@synchronized(self){
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return _instance;
}
@end
onceToken:
#import "Singleton2.h"
static Singleton2 *_instance;
@implementation Singleton2
+ (instancetype)share{
return [[self alloc]init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return _instance;
}
@end
苹果推荐使用GCD的一次性,效率高,而互斥锁效率低,下面比较一下两种方式的执行时间就一目了然了:
//@synchronized
//获取代码开始执行时时间
CFAbsoluteTime synBegin =CFAbsoluteTimeGetCurrent();
//获取代码结束执行时时间
Singleton *synSingle = [Singleton share];
CFAbsoluteTime synEnd =CFAbsoluteTimeGetCurrent();
//计算开始和结束的时间差,该时间差就是循环创建单例需要的时间
NSLog(@"synchronized------%f",synBegin- synEnd);
//onceToken
//获取代码开始执行时时间
CFAbsoluteTime onceBegin =CFAbsoluteTimeGetCurrent();
//获取代码结束执行时时间
Singleton2 *onceSingle = [Singleton share];
CFAbsoluteTime onceEnd =CFAbsoluteTimeGetCurrent();
//计算开始和结束的时间差,该时间差就是循环创建单例需要的时间
NSLog(@"onceToken---------%f",onceBegin- onceEnd);
打印结果
2018-04-03 15:18:01.046723+0800 GCD演示[1580:939403] synchronized-------0.000021
2018-04-03 15:18:01.046944+0800 GCD演示[1580:939403] onceToken----------0.000004
- 调度组
dispatch_group_t
GCD头文件group.h中谈到,可以将一组block提交到调度组(dispatch_group)中,执行逐个串行回调,下面来看看相关函数。
dispatch_group_t dispatch_group_create(void);
创建一个调度组,释放调度组使用dispatch_release()函数,创建成功返回一个dispatch_group调度组,失败则返回NULL.void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
提交一个闭包函数(block)到queue中,并关联到指定的group调度组.通过typedef void (^dispatch_block_t)(void);我们可以发现,该函数无法给block传递参数.
1.group 指定的调度组,block的关联调度组。
2.queue 提交闭包函数(block)的队列。
3.block 提交到指定queue的闭包函数block。void dispatch_group_async_f(dispatch_group_t group,dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
提交一个函数指针(dispatch_function_t)到queue中,并关联到指定的group调度组,函数返回void.
1.group 指定的调度组,block的关联调度组。
2.queue 提交闭包函数(block)的队列。
3.context 传递到函数中的的参数。
4.work 在指定的queue中的指定函数。long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
执行等待,等待所有关联到group调度组的block执行完成,或者等待timeout发生超时,当在超时时间timeout内执行完了所有的block函数,则返回0,否则返回非0值。
1.group 给定调度组
2.timeout 如果group调度组里边的block执行时间非常长,函数的等待时间.void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
该函数指定了一个block,当group调度组里边的所有block都执行完成时,将通知block关联到group中,并加入到给定的queue队列里,当group调度组当前没有任何block关联的时候将立即将block提交到queue队列,并与group调度组关联,该函数返回void.
1.group 给定的调度组
2.queue 给定的队列.
3.给定的闭包函数.void dispatch_group_notify_f(dispatch_group_t group,dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
与disptch_group_notify类似,提交的一个函数work作为执行体,context是执行时传递的参数,该函数返回void.void dispatch_group_enter(dispatch_group_t group);
void dispatch_group_leave(dispatch_group_t group);
这一对函数调用一次意味着非使用dispatch_group_async方式,将一个block提交到指定的queue上并关联到group调度组.两个函数必须成对出现。
在实际开发中,需要开启N个异步线程,但是后续操作,需要依赖N个线程返回的数据,需要接收所有线程任务执行完成的通知。
//1.对列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//2.调度组
dispatch_group_t group = dispatch_group_create();
//3.添加任务,让队列调度,任务执行情况,最后通知群组
dispatch_group_async(group, q, ^{
NSLog(@"Download A%@",[NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
sleep(1.0);
NSLog(@"Download B%@",[NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
NSLog(@"Download C%@",[NSThread currentThread]);
});
//所有任务完成之后通知群组
//用调度组,可以监听全局队列的任务,主队列去执行最后的任务
//dispatch_group_notify 本身也是异步执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@" OK %@",[NSThread currentThread]);
});
NSLog(@"come here");
注:dispatch_group_notify这个函数是异步的,如果要换成同步用dispatch_group_wait(group, DISPATCH_TIME_FOREVER).群组不空,这句代码一直等,下面代码不执行
// 队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 调度组
dispatch_group_t group = dispatch_group_create();
// 1. 进入群组,给 group 打一个标记,在后续紧接着的 block 归 group 监听
// dispatch_group_enter 和 dispatch_group_leave 必须成对出现!
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:10];
NSLog(@"download A - %@", [NSThread currentThread]);
// 耗时操作代码
// 2. 离开群组
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"download B - %@", [NSThread currentThread]);
// 耗时操作代码
// 2. 离开群组
dispatch_group_leave(group);
});
// 等待群组空,一直到永远,群组不空,这句代码就死等,同步
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"OK");
- 主队列
dispatch_get_main_queue()
主队列是用来在主线程上调度任务,会在程序开始时创建,只需要获取。
异步任务
//主队列 --> 已启动主线程就可以拿到主队列
dispatch_queue_t q = dispatch_get_main_queue();
//异步任务
dispatch_async(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
for (int i = 0; i < 10 ; i ++) {
NSLog(@"come here");
}
同步任务会造成死锁
//崩溃!!!!!!
dispatch_sync(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
for (int i = 0; i < 10 ; i ++) {
NSLog(@"come here");
}
解决办法
//主队列同步任务(不死锁)
void (^task)() = ^{
dispatch_queue_t q = dispatch_get_main_queue();
//2.异步任务
dispatch_sync(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
for (int i = 0; i < 10 ; i ++) {
NSLog(@"come here %@",[NSThread currentThread]);
}
};
dispatch_async(dispatch_get_global_queue(0, 0), task);