简介
上一篇已经介绍了iOS中的多线程基本原理和NSThread的基本使用,接下来我们简单介绍第二种开启线程的方式GCD,在了解GCD之前我们首先了解同步函数、异步函数、串行队列、并发队列
基本概念
GCD:它的全称是Grand Central Dispatch ,强大的中枢调度器。
同步函数:同步指的是只能在当前线程中执行任务,不具备开启新线程的能力。
异步函数:异步指的是可以在新的线程中执行任务,具备开始新线程的能力。
并发队列:并发队列指的是可以让多个任务并发(同时)执行,自动开启多个线程同时执行任务。
串行队列:串行队列指的是让任务一个接一个的执行,一个任务执行完毕才继续执行下一个。只要是带串的你们就可以联想成糖葫芦。
任务和队列结合,对应的组合特点
- 同步函数 + 主队列 (不用解释了肯定在主线程中执行)
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
- 异步函数 + 主队列:只在主线程中执行任务
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
- 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("ai.bobo.queue", DISPATCH_QUEUE_SERIAL);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
- 同步函数 + 并发队列:不会开启新的线程
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]);
});
- 异步函数 + 并发队列:可以同时开启多条线程
// 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]);
}
});
GCD线程中的通信
我们在开发中使用GCD 开启一条子线程执行耗时操作,然后子线程任务执行完之后获取到对应的数据,我们将数据展示到手机页面上,这样涉及到操作UI页面,那么必须回到主线程中进行操作。 其实也很简单,只需要将队列设置dispatch_get_main_queue() 就可以了,代码如下,模拟一个加载网络图片的操作。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"https://i1.hdslb.com/bfs/face/5cb955002eab27132543f9d8c86e8f3beac2ab4c.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
}
dispatch_barrier_async 栅栏的使用
在开发中我可能开启多个线程,但是有时候我在其中的一个线程中要用到其中一个线程中的数据,无法确认他们执行的顺序,苹果提供了dispatch_barrier_async这个函数,栅栏就是阻挡的意思,比较形象,就是在栅栏前的先执行,前面执行完了之后在执行栅栏函数和之后的任务。例如异步函数中有三条线程,线程3必须在线程1、2执行完成之后才执行,我们就可以使用栅栏挡一下。代码如下
- (void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("aibobo", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
//设置栅栏,让前面先执行
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
}
GCD中的延迟执行与执行一次的函数
- (void)delay
{
//dispatch_after方法 2.0 * NSEC_PER_SEC代表2秒之后执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run-----");
});
//还有其他方法实现:除了前面NSThread的睡的方法
//2秒之后在执行
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
//定时器也可以实现
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
}
//dispatch_once只执行一次,开发者常用于单例模式的设计中
- (void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"------run");
});
}
dispatch_group_async线程组,这个在开发中用的也比较多,用法的场景和dispatch_barrier_async一样,下面举例一个合成图片
- (void)group
{
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:@"网络图片1"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
self.image1 = [UIImage imageWithData:data];
});
// 2.下载图片2
dispatch_group_async(group, queue, ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"网络图片2"];
// 加载图片
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;
});
});
}