GCD的优势
为多核运算提出的解决方案,会更多利用多核CPU
会自定管理线程的生命周期。
GCD的两个核心概念 任务和队列
任务是执行什么操作 block
队列;用来存放任务 queue
GCD使用的两个步骤
1.定制任务:确定想做的事情。
2.将任务添加到队列中:指定运行的方式。
GCD会自动将列队只能的任务取出,放到对应的线程中执行,任务取出遵循FIFO原则:先进先出,后进后出
队列的类型 并发队列,串行队列,主队列
1.并发队列(Concurrent Dispatch Queue)
可以让多个任务同时执行(自动开启多个线程同时执行任务 );并发功能只有在异步(dispatch_async)函数下才有效.
如果当前调度的任务是同步执行的,会等待任务执行完成后,在调度后续的任务。
如果当前调度的任务是异步执行的,同时底层线程池有可用的线程资源,会再新的线程调度后续任务的执行
并行队列,同步执行
不开线程,顺序执行 ,与串行队列同步执行一样
并行队列,异步执行
开多个线程,异步执行
开多个线程,异步执行,每次开启多少个线程是不固定的(线程数,不由我们控制),线程数是由gcd来决定的
2.串行队列( Serial Dispatch Queue)
任务一个一个的执行,一个任务执行完才能执行下一个任务
无论队列中所指定的执行函数是同步还是异步都会等待前一个任务执行完成后,再调度后面的任务
使用dispatch_queue_create函数创建串行队列
dispatch_queue_create(const char *label,dispatch_queue_attr_t attr);
参数1 Label:队列名称
参数2 Attr:队列的属性 DISPATCH_QUEUE_SERIAL
串行队列,同步执行
不开线程,同步执行(在当前线程执行)
串行队列,异步执行
开一个线程,顺序执行
只有一个线程,因为是串行队列,只有一个线程就可以按顺序执行队列中的所有任务
3.主队列
特殊的串行队列,代表主线程
主队列,同步执行
程序执行不出来(死锁)
主队列:如果主线程正在执行代码,就不调度任务
同步执行:如果第一个任务没有执行,就继续等待第一个任务执行完成,再执行下一个任务此时互相等待,程序无法往下执行(死锁)
主队列,异步任务
不开线程,同步执行
主队列特点:如果主线程正在执行代码暂时不调度任务,等主线程执行结束后在执行任务
主队列又叫 全局串行队列
线程间通信示例
从子线程回到主线程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行耗时的异步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程,执行UI刷新操作
});
});
并发队列
GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建
使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_t dispatch_get_global_queue(
dispatch_queue_priority_t priority, // 队列的优先级
unsignedlong flags); // 此参数暂时无用,用0即可
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列
全局并发队列的优先级
#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 // 后台
全局队列和并发队列的区别
全局队列
没有名称
无论 MRC & ARC 都不需要考虑释放
日常开发中,建议使用"全局队列"
并发队列
有名字,和 NSThread 的 name 属性作用类似
如果在 MRC 开发时,需要使用 dispatch_release(q); 释放相应的对象
dispatch_barrier 必须使用自定义的并发队列
开发第三方框架时,建议使用并发队列
执行任务
GCD中有2个用来执行任务的函数,同步执行和异步执行
同步执行
dispatch_sync(dispathch_queue_t queue , dispath_block_t block);
异步执行任务
dispath_async(dispatch_queue_t queue , dispatch_block_t block);
并发和串行决定了任务的执行方式
同步和异步决定了要不要开启新的线程。同步是在当前线程中执行异步是在另一条线程中执行。
延时执行
ios常见的延时执行有2种方式
调用NSObject的方法
[ self performSelector:@selector(run) withObject: nil
aftetDelay:2.0 ]; 2秒后在调用selfe 的 run 的方法
使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
});
队列组
首先:分别异步执行2个耗时的操作
其次:等2个异步操作都执行完毕后,再回到主线程执行操作
如果想要快速高效地实现上述需求,可以考虑用队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});
单例模式
单例模式的作用
可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问;控制了实例的个数,节约系统资源
单例模式使用场合
在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//要初始化的代码
});
什么是RunLoop
RunLoop就是循环,每一个线程内部都有一个消息循环;只要主线程的消息循环默认开启,子线程的消息循环默认不开启
RunLoop的目的
保证程序不退出。
负责处理输入事件。
如果没有事件发生,会让程序进入休眠状态 。
RunLoop就是消息循环,每一个线程内部都有一个消息循环。
只有主线程的消息循环默认开启,子线程的消息循环默认不开启。
事件类型 分为 输入源和定时源
Input Sources (输入源) & Timer Sources (定时源)
使用总结
1.创建消息
2.把消息放入循环,并指定消息运行的模式
3.在与循环的模式匹配的时候,消息运行
子线程中的消息循环
特点:子线程默认不开启消息循环,主线程默认开启消息循环