什么是GCD?
纯C语言,提供了非常多强大的函数
GCD的优势
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核,四核)
GCD会自动管理线程的声明周期(创建线程,调度任务 ,销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
任务和队列
GCD中有2个核心概念
任务:执行什么操作
队列:用来存放任务
GCD的使用就2个步骤
1.定制任务:
确定想做的事情
2.将任务添加到队列中
GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循队列的FIFO
原则,先进先出,后进后出
执行任务GCD中有2个用来执行任务的常用函数
用同步的方式执行任务
queue:队列
block:任务
dispatch_sync(dispatch_queue_t queue, <#^(void)block#>);
用异步的方式执行任务
dispatch_async(dispatch_queue_t queue, <#^(void)block#>);
队列的类型GCD的队列可以分为2大类型
并发队列:
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
并发功能只有在异步(dispatch_async)函数下才有效
串行队列:
让任务一个接着一个地执行(一个任务执行完毕后,在执行下一个任务)
容易混淆的术语
有4个术语比较容易混淆:同步、异步、并发、串行
同步和异步主要影响:能不能开启新的线程
同步:只是在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力
并发和串行主要影响:任务的执行方式
并发:允许多个任务并发同时执行
串行:一个任务执行完毕后,再执行下一个任务
创建队列
创建普通队列
第一个参数:C语言的字符串,标签
第二个参数:队列的类型
DISPATCH_QUEUE_SERIAL 串行队列
DISPATCH_QUEUE_CONCURRENT 并发队列
dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
获得全局并发队列
第一个参数:优先级
第二个参数:此参数暂时无用,给未来使用,传0即可
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
使用Create函数创建的并发队列和全局并发队列的主要区别:
1.全局并发队列在整个应用程序中本身是默认存在的,并且对应有高优先级,默认优先级,低优先级和后台优先级一共四个并发队列,我们只是选择其中的一个直接拿来用。而Create函数是实打实的从头开始去创建一个队列
2.在iOS6.0之前,在GCD中凡是使用了带Create和retain的函数在最后都需要做一次release操作。而主队列和全局并发队列不需要我们手动release。当然了,在iOS6.0之后,GCD已经被纳入到了ARC的内存管理范畴中,即便是使用retain或者create函数创建的对象也不再需要开发人员手动释放,我们像对待普通OC对象一样对待GCD就可以了
3.在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效(没有给出具体原因)
四种使用方式
异步函数+并发队列
开启多条线程,队列中任务异步执行
并不是多少个任务就开多少个线程
- (void)asyncConcurrent
{
// 1.创建队列
// dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
// 获得全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.1>封装任务 2.2>添加任务到队列中
/**
第一个参数:队列
第二个参数:要执行的任务
*/
dispatch_async(queue, ^{
NSLog(@"download1--- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2--- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3-- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download4-- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download5-- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download6-- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download7-- %@",[NSThread currentThread]);
});
}
异步函数+串行队列
会开启新线程,队列中任务是串行执行
- (void)asyncSerial
{
dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"download1--- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2--- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3-- %@",[NSThread currentThread]);
});
}
同步函数+并发队列
不会开线程,任务是串行执行的
- (void)syncConcurrent
{
dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"download1--- %@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2--- %@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download3-- %@",[NSThread currentThread]);
});
}
同步函数+串行队列
不会开线程,任务是串行执行的
- (void)syncSerial
{
dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"download1--- %@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2--- %@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download3-- %@",[NSThread currentThread]);
});
}
GCD实现线程间通信
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.创建子线程下载图片
dispatch_async(dispatch_queue_create("com.520it.download", DISPATCH_QUEUE_CONCURRENT), ^{
NSURL *url = [NSURL URLWithString:@"http://pic.sc.chinaz.com/files/pic/pic9/201508/apic14052.jpg"];
NSData *imgData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imgData];
// 子线程中处理
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
// dispatch_async(dispatch_get_main_queue(), ^{
// self.imageView.image = image;
// });
});
}
GCD队列组
#import "ViewController.h"
#import "AFNetworking.h"
@interface ViewController ()
@property (nonatomic, strong) UIImage *image1;
@property (nonatomic, strong) UIImage *image2;
@property (nonatomic, strong) UIImageView *imageView;
@end
@implementation ViewController
- (UIImageView *)imageView{
if (!_imageView) {
_imageView = [[UIImageView alloc]init];
_imageView.frame = CGRectMake((self.view.frame.size.width - 200 )*0.5, (self.view.frame.size.height - 200 )*0.5, 200, 200);
[self.view addSubview:_imageView];
}
return _imageView;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self queueGroup3];
}
- (void)queueGroup3
{
/**
需求:
1.下载图片1,子线程
2.下载图片2,开子线程
3.合成并显示图片,开子线程
*/
// 1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.创建队列组
dispatch_group_t group = dispatch_group_create();
// 3.异步函数
// 1)封装任务
// 2)把任务添加到队列中
// 3)会监听任务的执行情况,通知group
dispatch_group_async(group, queue, ^{
NSLog(@"0-----%@",[NSThread currentThread]);
// 1.1确定url
NSURL *URL = [NSURL URLWithString:@"http://pic.sc.chinaz.com/files/pic/pic9/201508/apic14052.jpg"];
// 1.2下载二进制数据
NSData *imgData = [NSData dataWithContentsOfURL:URL];
// 1.3转换图片
self.image1 = [UIImage imageWithData:imgData];
});
dispatch_group_async(group, queue, ^{
// 2.1确定url
NSURL *URL = [NSURL URLWithString:@"http://pic.sc.chinaz.com/files/pic/pic9/201508/apic14052.jpg"];
// 2.2下载二进制数据
NSData *imgData = [NSData dataWithContentsOfURL:URL];
// 2.3转换图片
self.image2 = [UIImage imageWithData:imgData];
});
//3.合并图片
// 拦截通知,当队列组中所有的任务都执行完毕的时候会进入到下面的方法
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"notify-----%@",[NSThread currentThread]);
//3.1 创建图形上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
//3.2画图1
[self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
self.image1 = nil;
//3.3画图2
[self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
self.image2 = nil;
//3.4根据上下文得到一张图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//3.5关闭上下文
UIGraphicsEndImageContext();
//3.6更新UI
// dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
// });
});
}
//跟group1功能相同
- (void)queueGroup2
{
// 1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.创建队列组
dispatch_group_t group = dispatch_group_create();
// 3.再该方法后面的异步任务会被纳入到队列组的监听范围,进入群组
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
//离开群组
dispatch_group_leave(group);
});
//dispatch_group_enter(group);dispatch_group_leave(group);必须配对使用
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
//离开群组
dispatch_group_leave(group);
});
// 拦截通知,当队列组中所有的任务都执行完毕的时候会进入到下面的方法
// 问题?该方法是阻塞的吗? 内部本身是异步的,不是堵塞的
dispatch_group_notify(group, queue, ^{
NSLog(@"notify-----%@",[NSThread currentThread]);
});
// 等待 .死等.直到队列组中所有的任务都执行完毕之后才能执行
// 阻塞的
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"end");
}
- (void)queueGroup1
{
// 1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.创建队列组
dispatch_group_t group = dispatch_group_create();
// 3.异步函数
// 1)封装任务
// 2)把任务添加到队列中
// 3)会监听任务的执行情况,通知group
dispatch_group_async(group, queue, ^{
NSLog(@"0-----%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"1-----%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"2-----%@",[NSThread currentThread]);
});
// 拦截通知,当队列组中所有的任务都执行完毕的时候会进入到下面的方法
dispatch_group_notify(group, queue, ^{
NSLog(@"notify-----%@",[NSThread currentThread]);
});
}
@end
主队列的相关用法
使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列
放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列
跟普通的串行队列不一样
异步函数+ 主队列:所有任务都在主线程中不会开线程,任务是串行执行
- (void)asyncMain{
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"download4-- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download5-- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download6-- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download7-- %@",[NSThread currentThread]);
});
}
// 同步函数+ 主队列:
// 死锁原理:如果主队列发现当前主线程有任务在执行,那么主队列会暂停调用队列中的任务,直到主线程空闲为止
// 同步函数特点:如果同步函数没有执行完毕,那么后面的也别想执行
// 使用子线程调用即可解决
- (void)syncMain
{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"start");
dispatch_sync(queue, ^{
NSLog(@"download4-- %@",[NSThread currentThread]);
});
// dispatch_sync(queue, ^{
// NSLog(@"download5-- %@",[NSThread currentThread]);
// });
NSLog(@"end");
}