~ 写在正文之前:文章转移到翻这个墙中,希望继续关注啦。(2017.11.5)
(1) GCD基本概念
01 GCD
1.1 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”
纯C语言,提供了非常多强大的函数
1.2 GCD的优势
GCD是苹果公司为多核的并行运算提出的解决方案,自动利用更多的CPU内核(比如双核、四核)
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何管理线程的代码
02 两个核心概念-队列和任务
2.1 任务:需要执行的操作;任务是最小单位,任务一旦执行,就必须执行完毕;线程不同,线程可以暂停,阻塞,强制死亡。
2.2 队列:用来存放任务,任务的取出遵循队列的FIFO原则:先进先出,后进后出
2.3 任务、队列、线程、同异步函数的关系:
2.3.1任务:一个具体的任务(操作)
2.3.2队列:任务列表及执行顺序(并发队列:无序执行;串行队列:顺序执行)
2.3.3线程:车间工人,执行任务的个人(实际由CPU处理)
2.3.4同步与异步函数:决定执行任务的总人数
2.3.5总结:按什么顺序由多少人(线程)执行多少任务。
03 同步和异步(影响线程数)
3.1 同步:只能在当前线程中执行任务,不具备开启新线程的能力;在当前线程执行并且在所在队列中马上执行(重要)
3.2 异步:可以在新的线程中执行任务,具备开启新线程的能力(具备不代表一定开启新的线程),不要求马上执行
04 GCD的队列类型(影响执行顺序)
4.1 并发队列(Concurrent Dispatch Queue)
4.1.1 自动开启多个线程同时执行任务
4.1.2 并发功能只有在异步函数下才有效
4.2 串行队列(Serial Dispatch Queue)
4.2.1 一个任务执行完毕后,再执行下一个任务
4.3 主队列(跟主线程相关联的队列)
4.3.1 主队列是GCD自带的一种特殊的串行队列
4.3.2 在主队列中的任务,都会在主线程中执行
4.3.3 主线程主队列执行的任务中存在同步函数+主队列任务,会导致死锁(因为主队列中的当前任务并没有完成,它的下一个任务是新增的同步函数+主队列任务,并且要求所在线程立即执行所在队列(所在队列不是当前队列),系统无法在队列中的当前任务没处理完成前切换任务,导致死锁)
05 线程执行组合:
01 异步函数+并发队列:开启多条线程,并发执行任务
02 异步函数+串行队列:开启一条线程,串行执行任务
03 同步函数+并发队列:不开线程,串行执行任务
04 同步函数+串行队列:不开线程,串行执行任务
05 异步函数+主队列:不开线程,在主线程中串行执行任务
06 同步函数+主队列:不开线程,在主线程中串行执行任务(注意死锁发生)
07 使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列
(2)GCD基本使用【重点】
- 步骤(以什么顺序由多少人(线程)执行多少任务):
创建或获取队列
通过同步函数或异步函数或函数方式将任务添加到队列中
01 同步函数与异步函数
1.1 用同步(sync)的函数执行任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:队列
block:任务
1.2 用异步(async)的函数执行任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
queue:队列
block:任务
1.3 通过函数的方式来封装任务
/*
第一个参数:队列
第二个参数:要调用函数需要传递的参数
第三个参数:函数
*/
NSString *str = @"wendingding";
//桥接--(__)
dispatch_async_f(queue, (__bridge void *)(str), run);
02 并发队列与串行队列
2.1 手动创建并发队列
/*
第一个参数:C语言的字符串 队列名称
第二个参数:队列的类型
DISPATCH_QUEUE_CONCURRENT--并发
DISPATCH_QUEUE_SERIAL---串行队列
*/
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
2.2 获得全局并发队列
2.2.1GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
/*
第一个参数:队列的优先级
第二个参数:此参数是留给未来使用,暂时无用,用0即可
DISPATCH_QUEUE_CONCURRENT--并发
DISPATCH_QUEUE_SERIAL---串行队列
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2.2.2 全局并发队列的优先级
#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 // 后台
2.3 串行队列
/*
第一个参数:C语言的字符串 队列名称
第二个参数:队列的类型
DISPATCH_QUEUE_CONCURRENT--并发
DISPATCH_QUEUE_SERIAL---串行队列或传递NULL
*/
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
2.4 使用主队列(跟主线程相关联的队列)
//使用dispatch_get_main_queue()获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
(3)GCD线程间通信
//0.获取一个全局的队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.先开启一个线程,把下载图片的操作放在子线程中处理
dispatch_async(queue, ^{
//2.下载图片
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"下载操作所在的线程--%@",[NSThread currentThread]);
//3.回到主线程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
//打印查看当前线程
NSLog(@"刷新UI---%@",[NSThread currentThread]);
});
});
(4)GCD其它常用函数
01 栅栏函数(控制任务的执行顺序)
dispatch_barrier_sync(queue, ^{
NSLog(@"--dispatch_barrier_sync-");
});
//在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
//这个queue不能是全局的并发队列,苹果文档没做解释
02 延迟执行(延迟·控制在哪个线程执行)
2.1 第一种方法——使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"---%@",[NSThread currentThread]);
});
2.2 第二种方法——调用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后再调用self的run方法
2.3 第三种方法——使用NSTimer
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:NO];
03 一次性代码(注意不能用在懒加载)
-(void)once
{
//整个程序运行过程中只执行一次,默认是线程安全的
//onceToken用来记录该部分的代码是否被执行过
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"-----");
});
}
04 快速迭代(开多个线程并发完成迭代操作)
dispatch_apply(subpaths.count, queue, ^(size_t index) {
// index顺序不确定
// 内部开多个线程并发完成迭代操作,有可能包括主线程
// 虽然是多线程,但一样会卡住线程
// 应用场景:快速处理某些不算太耗时的操作,并回到主线程进行某些操作。
// 如果处理耗时操作,建议使用并发+异步函数,手动回到主线程刷新,不建议使用这种方法
});
05 队列组(同栅栏函数)
//创建队列组
dispatch_group_t group = dispatch_group_create();
//多次执行耗时的异步操作
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
//队列组中的任务执行完毕之后,执行该函数
dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
(5)快速迭代剪切文件
-(void)apply
{
//1.创建文件管理者
NSFileManager *manager = [NSFileManager defaultManager];
NSString *from = @"/Users/a1/Desktop/form";
NSString *to = @"/Users/a1/Desktop/to";
//2.获得要剪切的所有文件
NSArray *subPaths =[manager subpathsAtPath:from];
NSLog(@"%@",subPaths);
NSInteger count = subPaths.count;
//3.创建并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 4.快速迭代
// 内部不能使用continue
/*
第一个参数:循环的次数
第二个参数:队列
第三个参数:block 在里面执行迭代任务 index:索引
*/
dispatch_apply(count, queue, ^(size_t index) {
//剪切图片
//4.1获得文件名称
NSString *fileName = subPaths[index];
//4.2 拼接要剪切的文件的全路径
NSString *fullPath1 = [from stringByAppendingPathComponent:fileName];
//4.3 拼接文件要剪切到哪个地方的全路径
NSString *fullpath2 = [to stringByAppendingPathComponent:fileName];
//4.4 执行剪切
[manager moveItemAtPath:fullPath1 toPath:fullpath2 error:nil];
NSLog(@"%@---%@---%@",[NSThread currentThread],fullPath1,fullpath2);
});
}
(6)GDC其他内容
01 使用Crearte函数创建的并发队列和全局并发队列的主要区别:
1. 全局并发队列在整个应用程序中本身是默认存在的,并且对应有高优先级、默认优先级、低优先级和后台优先级一共四个并发队列,我们只是选择其中的一个直接拿来用。
2. Crearte函数是实打实的从头开始去创建一个队列。
02 GCD内存管理
1. 在iOS6.0之前,在GCD中凡是使用了带Crearte和retain的函数在最后都需要做一次release操作。而主队列和全局并发队列不需要我们手动release。
2. 在iOS6.0之后GCD已经被纳入到了ARC的内存管理范畴中,即便是使用retain或者create函数创建的对象也不再需要开发人员手动释放,我们像对待普通OC对象一样对待GCD就OK。
03 在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效(没有给出具体原因)
04 其它区别涉及到XNU内核的系统级线程编程,不一一列举
05 给出一些参考资料(可以自行研究):
GCDAPI:https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html#//apple_ref/c/func/dispatch_queue_create
Libdispatch版本源码:http://www.opensource.apple.com/source/libdispatch/libdispatch-187.5/