1.GCD的核心概念
GCD 核心概念:将任务添加到队列,指定任务执行的方法
- 任务
- 使用block 封装
- block 就是一个提前准备好的代码块,在需要的时候执行
- 任务的取出遵循队列的FIFO原则:先进先出
- 队列(负责调度任务)
- 串行队列: 一个接一个的调度任务
- 并发队列: 可以同时调度多个任务
- 任务执行函数(任务都需要在线程中执行!!)
- 同步执行: 不会到线程池里面去获取子线程!
- 异步执行: 只要有任务,CPU就会到线程池取子线程!(主队列除外!)
小结: - 开不开线程,取决于执行任务的函数,同步不开,异步才能开
- 开几条线程,取决于队列,串行开一条,并发可以开多条(异步)
2. 串行队列,同步执行
不会开启子线程,会顺序执行
/**
1.队列名称:
2.队列的属性: DISPATCH_QUEUE_SERIAL 表示串行! NULL默认就表示串行!
*/
dispatch_queue_t q = dispatch_queue_create("ios", NULL);
//2.同步执行任务
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
打印结果如下:可以看出没有开启新线程,并且按顺序执行3.串行队列,异步执行
会开新线程,会顺序执行
//1.队列 - 串行
dispatch_queue_t q = dispatch_queue_create("ios", NULL);
//2.异步执行任务
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//在主线程!
NSLog(@"come here");
4. 并发队列,异步执行
会开启多条线程,异步执行
//1.队列 - 并发 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
//2.异步执行任务
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//在主线程!
NSLog(@"come here");
5. 并发队列,同步执行
和 串行队列,同步执行 效果一样!
// 会开线程吗? 顺序执行? come here?
// 不会 顺序 最后
//1.队列 - 并发 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
//2.同步执行任务
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//在主线程!
NSLog(@"come here");
6. 同步任务的作用
在开发中,通常会将耗时操作放后台执行,有的时候,有些任务彼此有"依赖"关系!
例子: 登录,支付,下载
利用同步任务,能够做到任务依赖关系,前一个任务是同步任务,不执行完,队列就不会调度后面的任务
dispatch_queue_t loginQueue = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
//1.用户登录
dispatch_sync(loginQueue, ^{
NSLog(@"用户登录 %@",[NSThread currentThread]);
});
//2.支付
dispatch_async(loginQueue, ^{
NSLog(@"支付 %@",[NSThread currentThread]);
});
//3.下载
dispatch_async(loginQueue, ^{
NSLog(@"下载 %@",[NSThread currentThread]);
});
for (int i = 0; i< 10; i++) {
NSLog(@"......%@",[NSThread currentThread]);
}
7. 同步任务增强
可以队列调度多个任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这就是依赖关系
//队列
dispatch_queue_t q = dispatch_queue_create("tanzhouios", DISPATCH_QUEUE_CONCURRENT);
//任务
void (^task)()=^{
for (int i = 0; i < 10; i++) {
NSLog(@"%d %@",i ,[NSThread currentThread]);
if (i==5) {
//1.用户登录
dispatch_sync(q, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"用户登录 %@",[NSThread currentThread]);
}
});
}
}
//2.支付
dispatch_async(q, ^{
NSLog(@"支付 %@",[NSThread currentThread]);
});
//3.下载
dispatch_async(q, ^{
NSLog(@"下载 %@",[NSThread currentThread]);
});
};
dispatch_async(q, task);
8.全局队列
1.本质上并发队列
2.创建一个全局队列方法
dispatch_get_global_queue(long identifier, unsigned long flags)
参数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.
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
for (int i = 0; i< 10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
NSLog(@"come here");
9.全局队列 & 并发队列区别
1> 名称,并发队列可以取名字,适合于企业开发跟踪错误
2> release,在MRC 中使用并发队列 需要release
dispatch_release(q)
ARC 情况下不需要release !
全局队列 & 串行队列
全局队列: 并发,能够调度多个线程,执行效率高
- 费电
串行队列:一个一个执行,执行效率低
- 省电
判断依据:用户上网方式
- WIFI : 可以多开线程 5~6条
- 流量 : 尽量少开线程 2~3条
10.GCD延时执行
从现在开始,进过多少纳秒之后,让 queue队列,调度 block 任务,异步执行!
参数:
1.when
2.queue
3.block
NSLog(@"come here");
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.00003 * NSEC_PER_SEC));
dispatch_after(when, dispatch_queue_create("ios", NULL), ^{
NSLog(@"%@",[NSThread currentThread]);
});
11. GCD一次执行
苹果提供的 一次执行机制,不仅能够保证一次执行!而且是线程安全的!!
苹果推荐使用 gcd 一次执行,效率高
不要使用互斥锁,效率低!
从下图可以看出,只执行一次
在执行前onceToken值为0,执行后值为-1
for (int i = 0 ; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self once];
});
}
-(void)once{
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
dispatch_once(&onceToken, ^{
//只会执行一次!!
NSLog(@"执行了%@",[NSThread currentThread]);
});
}
11.GCD调度组
//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, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"download B%@",[NSThread currentThread]);
});
dispatch_group_async(group, q, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"download C%@",[NSThread currentThread]);
});
//4.所有任务执行完毕后,通知
//用一个调度组,可以监听全局队列的任务,主队列去执行最后的任务
//dispatch_group_notify 本身也是异步的!
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//更新UI,通知用户
NSLog(@"下载完成,合并图片 %@",[NSThread currentThread]);
});
通常情况下,在group中加入任务block是这样的
dispatch_group_async(group, queue, ^{
block();
});
这个写法等价于
dispatch_async(queue, ^{
dispatch_group_enter(group);
block()
dispatch_group_leave(group);
});
如果要把一个异步任务加入group,这样就行不通了:
dispatch_group_async(group, queue, ^{
[self performBlock:^(){
block();
}];
//未执行到block() group任务就已经完成了
});
这时需要这样写:
dispatch_group_enter(group);
[self performBlock:^(){
block();
dispatch_group_leave(group);
}];
异步任务回调后才算这个group任务完成
12.主队列
主队列 & 串行队列的区别
都是 一个一个安排任务
队列特点:FIFO
并发队列: 可以调度很多任务
串行队列,:必须等待一个任务执行完成,再调度另外一个,最多只能开启一条线程
主队列:以FIFO调度任务,如果主线程上有任务在执行,主队列就不会调度任务,主要是负责在主线程上执行任务
//主队列是专门负责在主线程上调度任务的队列 --> 不会开线程
//1.队列 --> 已启动主线程,就可以获取主队列
dispatch_queue_t q = dispatch_get_main_queue();
//2.异步任务
dispatch_async(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"come here");
// 这种当时执行主队列,同步会造成死锁
NSLog(@"这里!!");
//1.队列 --> 已启动主线程,就可以获取主队列
dispatch_queue_t q = dispatch_get_main_queue();
//2.同步任务 ==> 死锁
dispatch_sync(q, ^{
NSLog(@"能来吗? ");
});
NSLog(@"come here");
主队列同步任务(不死锁的),这种情况下不会造成死锁
void (^task)() = ^{
NSLog(@"这里!!%@",[NSThread currentThread]);
//1.队列 --> 已启动主线程,就可以获取主队列
dispatch_queue_t q = dispatch_get_main_queue();
//2.同步任务
dispatch_sync(q, ^{
NSLog(@"能来吗? %@",[NSThread currentThread]);
});
NSLog(@"come here");
};
//会开线程吗??
dispatch_async(dispatch_get_global_queue(0, 0), task);
13.各种队列的执行效果
注意
使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列
14 dispatch_barrier_async
dispatch_barrier_async的作用?
在并行队列中,为了保持某些任务的顺序,需要等待一些任务完成之后才能继续执行,使用barrier来等待之前任务完成,避免数据竞争等问题。
dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后在执行barrier函数的处理,执行结束之后再回复之前的动作继续执行。
注意:
使用dispatch_barrier_async,该函数只能搭配自定义并行队列
dispatch_queue_t使用,不能使用:
dispatch_get_global_queue,否则dispatch_barrier_async的作用会和dispatch_async一模一样。
15.信号量dispatch_semaphore
信号量使用
信号量:Semaphore是通过计数
的方式来标识线程是否是等待或继续执行的。
dispatch_semaphore_create(int) // 创建一个信号,并初始化信号的计数大小
/* 等待信号,并且判断信号量,如果信号量计数大于等于你创建时候的信号量的计数,就可以通过,继续执行,并且将你传入的信号计数减1,
* 如果传入的信号计数小于你创建的计数,就表示等待,等待信号计数的变化
* 如果等待的时间超过你传入的时间,也会继续下面操作
* 第一个参数:semaphore 表示信号量
* 第二个参数:表示等待的时间
* 返回int 如果传入的信号计数大于等于你创建信号的计数时候,返回0. 反之,返回的不等于0
*/
int result = dispatch_semaphore_wait(dispatch_semaphore_t semaphore,time outTime);// 表示等待,也是阻碍线程
// 表示将信号计数+1
dispatch_semaphore_signl(dispatch_semaphore_t semaphore);
16.dispatch的其他用法
使用dispatch_apply函数能进行快速迭代遍历
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
// 执行10次代码,index顺序不确定
});