GCD基本思想
- GCD的基本思想是就将操作放在队列中去执行
- 操作使用Blocks定义
- 队列负责调度任务执行所在的线程以及具体的执行时间
- 队列的特点是先进先出(FIFO)的,新添加至对列的操作都会排在队尾
GCD中的专用术语
同步函数和异步函数主要影响:能不能开启新的线程
- 同步函数:只是在当前线程中执行任务,不具备开启新线程的能力
- 异步函数:可以在新的线程中执行任务,具备开启新线程的能力(特殊情况:在主队列中不会开启新线程)
并发队列和串行队列主要影响:任务的执行方式
- 并发队列:允许多个任务并发(同时)执行
- 穿行队列:一个任务执行完毕后,再执行下一个任务
同步函数 && 异步函数的小差别
- (void)syncConcurrent
{
// 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncConcurrent--------end");// 先打印上面的,在打印这一句
}
- (void)asyncConcurrent
{
// 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"1-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"2-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"3-----%@", [NSThread currentThread]);
}
});
NSLog(@"asyncConcurrent--------end");// 先打印这一句,再打印上面的
// dispatch_release(queue);
}
NSLog(@”syncConcurrent——–end”);
NSLog(@”asyncConcurrent——–end”);
明白上面两行都是什么时候调用
同步函数:任务加入队列后,立刻执行
异步函数:任务加入队列后,等会执行,可以理解为asyncConcurrent函数执行完,在执行任务
串行队列&&主队列
GCD中获得串行队列有两种途径
使用
dispatch_queue_create
函数创建串行队列
// 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("AHUAN.queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("AHUAN.queue", NULL);
dispatch_queue_t queue = dispatch_queue_create("AHUAN.queue", NULL);
// 同步操作不会新建线程、操作顺序执行(一般不用)
dispatch_sync(queue , ^{
NSLog(@"串行同步 %@", [NSThread currentThread]);
});
// 异步操作会新建一个线程、操作顺序执行(非常有用!)场景:既不影响主线程,又需要顺序执行的操作!
dispatch_async(queue , ^{
NSLog(@"串行异步 %@", [NSThread currentThread]);
});
- 使用主队列(跟住线程相关联的队列)
- 主队列是GCD自带的一种特殊的串行队列
- 放在主队列中的任务,都会放到主线程中
- 使用
dispatch_get_main_queue()
获得主队列
// 每一个应用程序对应唯一一个主队列,直接GET即可,在多线程开发中,使用主队列更新UI
dispatch_queue_t queue = dispatch_get_main_queue();
// 主队列中的操作都应该在主线程上顺序执行的,不存在异步的概念
dispatch_async(queue, ^{
NSLog(@"主队列异步 %@", [NSThread currentThread]);
});
// 主队列中添加的同步操作永远不会被执行,会死锁
dispatch_sync(queue, ^{
NSLog(@"主队列同步 %@", [NSThread currentThread]);
});
并发队列&&全局队列
- GCD中获得并发队列也有两种途径
- 手动创建并发队列
dispatch_queue_t queue = dispatch_queue_create("cn.itcast.demoqueue", DISPATCH_QUEUE_CONCURRENT);
// 同步操作不会新建线程,操作顺序执行
dispatch_sync(queue, ^{
NSLog(@"并行同步 %@", [NSThread currentThread]);
});
// 异步操作会新建多个线程、操作无序执行(有用,容易出错!)
// 队列前如果有其他任务,会等待前面的任务完成之后再执行
// 场景:既不影响主线程,又不需要顺序执行的操作!
dispatch_async(queue, ^{
NSLog(@"并行异步 %@", [NSThread currentThread]);
});
- GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
// 全局队列是系统的,直接拿过来(GET)用就可以
// 与并行队列类似
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 优先级 0 就代表 DISPATCH_QUEUE_PRIORITY_DEFAULT
不同队列中嵌套dispatch_sync的结果
// 全局队列,都在主线程上执行,不会死锁
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 并行队列,都在主线程上执行,不会死锁
dispatch_queue_t q = dispatch_queue_create("AHUNA.queue",DISPATCH_QUEUE_CONCURRENT);
// 串行队列,会死锁,但是会执行嵌套同步操作之前的代码
dispatch_queue_t q = dispatch_queue_create("AHUNA.queue", DISPATCH_QUEUE_SERIAL);
// 直接死锁
dispatch_queue_t q = dispatch_get_main_queue();
dispatch_sync(q, ^{
NSLog(@"同步任务 %@", [NSThread currentThread]);
dispatch_sync(q, ^{
NSLog(@"同步任务 %@", [NSThread currentThread]);
});
});
线程间的通信示例
- 从子线程回到主线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 执行耗时的异步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程,执行UI刷新操作
});
});
GCD常用函数
执行任务的函数dispatch_barrier_async
执行任务的函数
dispatch_barrier_async(<#dispatch_queue_t queue#>, ^{
<#code#>
})
- 在这之前的任务结束后它才执行,而且它后面的任务等它执行完之后才会执行
- 这个queue不能是全局的并发队列,可以是我们自己创建的并发队列
一次性代码dispatch_once
- 确保某段代码在程序运行过程中只被执行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 这里面的代码整个程序只执行一次(这里面默认是线程安全的)
});
延时执行
- 使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后执行这里的代码...
});
- 调用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后在调用self的run方法
- 使用NSTimer
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
// 2秒后在调用self的run方法
快速迭代
- 使用dispatch_apply函数能进行快速迭代遍历
// size_t iterations代表执行多少次
dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t queue#>, ^(size_t index) {
<#code#>
// index顺序不确定
});
组队列
// 分别异步执行两个耗时的操作(下载图片),等2个异步操作都执行完毕后,再异步执行一个耗时操作(合成图片)
// 然后在回到主线程中刷新UI界面
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 创建一个队列组
dispatch_group_t group = dispatch_group_create();
// 1.下载图片1
dispatch_group_async(group, queue, ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
self.image1 = [UIImage imageWithData:data];
});
// 2.下载图片2
dispatch_group_async(group, queue, ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
self.image2 = [UIImage imageWithData:data];
});
// 3.将图片1、图片2合成一张新的图片
dispatch_group_notify(group, queue, ^{
// 开启新的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
// 绘制图片
[self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
[self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
// 取得上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 结束上下文
UIGraphicsEndImageContext();
// 回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
// 4.将新图片显示出来
self.imageView.image = image;
});
});
单粒模式
- 在
.m
中保留一个全局的static的实例:static id_instance
// 重写allocWithZone:方法,在这里创建一个唯一的实例(注意线程安全)
+(instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
// 提供1个类方法让外界访问唯一的实例
+ (instancetype)sharedInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
// 实现copyWithZone:方法
- (id)copyWithZone:(struct _NSZone *)zone{
return _instance;
}