GCD 的一些简介
- 什么是GCD:
1.GCD 全称是Grand Central Dispatch, 可以译为“牛逼的中枢调度器”,并不是多线程技术
2.纯C语言,提供了非常多的强大的函数 - GCD的优势
1.GCD是苹果公司为多核的并行运算提出的解决方案
2.GCD会自动利用更多的CPU内核(比如双核、四核)
3.GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
4.程序员只需告诉GCD想要执行什么任务,不需要编写任何线程管理代码
GCD 的队列(四种)
串行队列
dispatch_queue_t q = dispatch_queue_create("threadname", NULL);
并行队列
dispatch_queue_t q = dispatch_queue_create("threadname", DISPATCH_QUEUE_CONCURRENT);
全局队列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
-
主队列
dispatch_queue_t q = dispatch_get_main_queue();
- 全局队列 & 并发队列
1> 名称,并发队列取名字,适合于企业开发跟踪错误
2> release,在MRC 并发队列 需要使用的
dispatch_release(q);//ARC 情况下不需要release !
- 全局队列 & 串行队列
全局队列: 并发,能够调度多个线程,执行效率高
- 费电
串行队列:一个一个执行,执行效率低
- 省点
- 判断依据:用户上网方式
- WIFI : 可以多开线程
- 流量 : 尽量少开线程
- 全局队列 & 并发队列
GCD 队列任务常见代码
1.串行队列,同步任务
/**
* 不会开启线程,会顺序执行
*/
-(void)gcdDemo1{
//1.队列 - 串行
/**
1.队列名称:
2.队列的属性: DISPATCH_QUEUE_SERIAL 标示串行!
*/
dispatch_queue_t q = dispatch_queue_create("thread1", NULL);
//2.同步执行任务
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
2.串行队列,异步任务
-(void)gcdDemo2{
/**
会开几条线程?会顺序执行吗?
*/
//1.队列 - 串行
dispatch_queue_t q = dispatch_queue_create("thread1", NULL);
//2.异步执行任务
for (int i = 0; i < 10; i++) {
NSLog(@"%d------------",i);
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//哥么在主线程!
NSLog(@"come here");
}
3.并发队列,异步执行
-(void)gcdDemo3{
//1.队列 - 并发 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("thread1", DISPATCH_QUEUE_CONCURRENT);
//2.异步执行任务
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//哥么在主线程!
NSLog(@"come here");
}
4.并发队列,同步执行
和 串行队列,同步执行 效果一样!
-(void)gcdDemo4{
//1.队列 - 并发 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("thread1", DISPATCH_QUEUE_CONCURRENT);
//2.同步执行任务
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//哥么在主线程!
NSLog(@"come here");
}
GCD同步任务的作用-- 处理任务之间的依赖关系
在开发中,通常会将耗时操作放后台执行,有的时候,有些任务彼此有"依赖"关系!
例子: 登录,支付,下载
利用同步任务,能够做到任务依赖关系,前一个任务是同步任务,哥么不执行完,队列就不会调度后面的任务
-(void)gcdDemo5{
dispatch_queue_t loginQueue = dispatch_queue_create("thread1", DISPATCH_QUEUE_CONCURRENT);
//1.用户登录
dispatch_sync(loginQueue, ^{
NSLog(@"用户登录 %@",[NSThread currentThread]);
});
//2.支付
dispatch_async(loginQueue, ^{
NSLog(@"支付 %@",[NSThread currentThread]);
});
//3.下载
dispatch_async(loginQueue, ^{
NSLog(@"下载 %@",[NSThread currentThread]);
});
}
- 增强版同步任务
可以队列调度多个任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这就是依赖关系
- 同步任务,会造成一个死锁!
-(void)gcdDemo6{
//队里
dispatch_queue_t q = dispatch_queue_create("thread1", DISPATCH_QUEUE_CONCURRENT);
//任务
void (^task)()=^{
for (int i = 0; i < 10; i++) {
NSLog(@"%d %@",i ,[NSThread currentThread]);
}
//1.用户登录
dispatch_sync(q, ^{
NSLog(@"用户登录 %@",[NSThread currentThread]);
});
//2.支付
dispatch_async(q, ^{
NSLog(@"支付 %@",[NSThread currentThread]);
});
//3.下载
dispatch_async(q, ^{
NSLog(@"下载 %@",[NSThread currentThread]);
});
};
dispatch_async(q, task);
}
GCD 主队列 死锁问题
- 死锁版
// 主队列,同步任务
//阻塞主线程!!
-(void)gcdDemo2{
//1.队列 -> 一启动就有主线程,主队列只需要获取
dispatch_queue_t q = dispatch_get_main_queue();
NSLog(@"这里!!");
//2.同步任务 -----> 死锁
dispatch_sync(q, ^{
NSLog(@"能来吗?");
});
NSLog(@"come here -- %@",[NSThread currentThread]);
}
- 不死锁版
//MARK: 主队列同步任务(增强版--不死锁)
-(void)gcdDemo3{
void (^task)() = ^{
//1.队列 -> 一启动就有主线程,主队列只需要获取
dispatch_queue_t q = dispatch_get_main_queue();
NSLog(@"这里!!");
//2.同步任务
dispatch_sync(q, ^{
NSLog(@"睡会");
[NSThread sleepForTimeInterval:1.0];
NSLog(@"能来吗?");
});
NSLog(@"come here -- %@",[NSThread currentThread]);
};
//全局队列,异步任务
dispatch_async(dispatch_get_global_queue(0, 0), task);
}
线程间通信式例
-(void)gcdDemo{
//指定任务执行方法 -- 异步
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//更新UI 主队列,就是专门负责在主线程上调度任务的队列!
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"更新UI%@",[NSThread currentThread]);
});
});
}
GCD 一次性代码
- 有的时候,我们在开发中,有些代码,从程序启动之后,就只希望执行一次,
尤其在使用单例设计的时候,使用GCD来让代码一次执行的情况,非常多!
面试非常流行,手写GCD的单例!
-(void)once{
//GCD提供了一个一次执行的机制,不仅能够保证只会被执行一次.而且是线程安全的!
//GCD-Once 内部是会加锁!! 但是 比普通的互斥锁 效率高 100多倍 苹果推荐使用这个
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
dispatch_once(&onceToken, ^{
//只会执行一次的代码
NSLog(@"执行了!!--%@",[NSThread currentThread]);
});
}
调度组
-(void)group1{
//1.队列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//2.调度组
dispatch_group_t g = dispatch_group_create();
//3.添加任务,让队列执行,任务执行情况最终通知组
dispatch_group_async(g, q, ^{
NSLog(@"download A %@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"download B %@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
[NSThread sleepForTimeInterval:0.8];
NSLog(@"download C %@",[NSThread currentThread]);
});
//4. 所有任务执行完毕后,获得通知
//用一个调度组,可以监听全局队列调度的任务,执行完毕之后,在主队列执行最终处理!
//dispatch_group_notify 本身是异步的
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
//更新UI,通知用户
NSLog(@"OK %@",[NSThread currentThread]);
});
NSLog(@"come here");
}
全局队列参数
//全局队列
/* 参数
1. 涉及到系统适配
iOS 8 服务质量
QOS_CLASS_USER_INTERACTIVE 用户交互(希望线程快速被执行,不要用好使的操作)
QOS_CLASS_USER_INITIATED 用户需要的(同样不要使用耗时操作)
QOS_CLASS_DEFAULT 默认的(给系统来重置队列的)
QOS_CLASS_UTILITY 使用工具(用来做耗时操作)
QOS_CLASS_BACKGROUND 后台
QOS_CLASS_UNSPECIFIED 没有指定优先级
iOS 7 调度的优先级
- DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
- DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
- DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
- DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
提示:尤其不要选择BACKGROUND 优先级,服务质量,线程执行会慢到令人发指!!!
2. 为未来使用的一个保留,现在始终给0.
老项目中,一般还是没有淘汰iOS 7 ,没法使用服务质量
*/
GCD 总结
GCD 核心概念:将任务添加到队列,指定任务执行的方法
- 任务
- 使用block 封装
- block 就是一个提前准备好的代码块,在需要的时候执行
- 队列(负责调度任务)
- 串行队列: 一个接一个的调度任务
- 并发队列: 可以同时调度多个任务
- 任务执行函数(任务都需要在线程中执行!!)
- 同步执行: 不会到线程池里面去获取子线程!
- 异步执行: 只要有任务,哥么就会到线程池取子线程!(主队列除外!)
小结: - 开不开线程,取决于执行任务的函数,同步不开,异步才能开
- 开几条线程,取决于队列,串行开一条,并发可以开多条(异步)