GCD详解

简介:

GCD(全称Grand CentralDispatch) 
纯C语言

GCD优势:

1.为多核运算提供
2.自动管理线程的生命周期(创建线程、调度任务、销毁线程、),相比NSTread需要手动管理线程和生命周期更方便
3.只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

GCD中有两个核心概念: 任务和队列

1.任务: 执行什么操作
2.队列: 用来存放任务 (串行队列、并发队列)
3.将任务添加到队列,GCD会自动将队列中任务取出,放到对应线程中执行,任务取出遵循:先进先出,后进后出

 

提前说一下参数类型:

第一个参数: 队列的名称
第二个参数: 告诉系统需要创建一个并发队列还是串行队列
DISPATCH_QUEUE_SERIAL :串行
DISPATCH_QUEUE_CONCURRENT 并发
第一个参数: iOS8以前是优先级, iOS8以后是服务质量
iOS8以前
*  - DISPATCH_QUEUE_PRIORITY_HIGH          高优先级 2
*  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      默认的优先级 0
*  - DISPATCH_QUEUE_PRIORITY_LOW:          低优先级 -2
*  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:

iOS8以后
*  - QOS_CLASS_USER_INTERACTIVE  0x21 用户交互(用户迫切想执行任务)
*  - QOS_CLASS_USER_INITIATED    0x19 用户需要
*  - QOS_CLASS_DEFAULT           0x15 默认
*  - QOS_CLASS_UTILITY           0x11 工具(低优先级, 苹果推荐将耗时操作放到这种类型的队列中)
*  - QOS_CLASS_BACKGROUND        0x09 后台
*  - QOS_CLASS_UNSPECIFIED       0x00 没有设置

第二个参数: 废物

  

执行任务: 同步、异步、栅栏

同步 dispatch_sync : 只能在当前线程中执行任务,不具备开启新线程的能力

dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)

异步 dispatch_async:可以在新的线程中执行任务,具备开启新线程的能力

dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)

另外一个执行任务的方法:dispatch_barrier_async 栅栏

dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)

(场景:在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行 注意:这个queue不能是全局的并发队列)

队列:串行与并行

使用dispatch_queue_create函数创建队列

dispatch_queue_t dispatch_queue_create(
    const char *Label,//队列名称
    dispatch_queue_attr_t attr//队列的类型
);

并发队列: 多任务同时执行,开启多个线程

1.手动创建并发队列dispatch_queue_create

dispatch_queue_t queue = dispatch_queue_create(@"队列名称", DISPATCH_QUEUE_CONCURRENT);

2.GCD默认提供全局并发队列dispatch_get_global_queue

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

串行队列:任务一个个的执行

1.手动创建串行队列dispatch_queue_create

dispatch_queue_t queue = dispatch_queue_create(@"队列名称", NULL);

2.系统自带一种特殊串行队列

dispatch_queue_t queue = dispatch_get_main_queue();

同步、异步区别

同步: 在当前线程中执行任务,不开辟新的线程
异步:可以在新的线程中执行任务,可以开辟新线程

并发、串行区别

并发: 允许多个任务并发执行
串行: 一个任务执行完之后再执行下一个任务

同步函数 + 主队列(在子队列中进行)

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
    //block会在子线程中执行
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        //block一定会在主线程执行
    });
});

异步 + 主队列: 不会创建新的线程,并且任务都是在主线程中执行

dispatch_queue_t queue = dispatch_get_main_queue();
   dispatch_async(queue, ^{
    //只要任务添加到主队列中,那么任务就一定会在主线程中执行
});

同步 + 并发 : 不会创建新的线程

//创建一个并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//将任务添加到队列中
dispatch_sync(queue, ^{
    NSLog(@"任务1");
});
dispatch_sync(queue, ^{
    NSLog(@"任务2");
});
dispatch_sync(queue, ^{
    NSLog(@"任务3");
});
NSLog(@"任务");
输出: 任务1 任务2 任务3 任务

 同步 + 串行 : 不会创建新的线程(如果调用同步函数,会等同步函数中得任务执行完之后再执行后面的的代码)

//创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("aaa", NULL);
//将任务添加到队列中
dispatch_sync(queue, ^{
    NSLog(@"任务1");
});
dispatch_sync(queue, ^{
    NSLog(@"任务2");
});
dispatch_sync(queue, ^{
    NSLog(@"任务3");
});
NSLog(@"任务");

异步 + 串行 : 会开启新的线程(只有一个) 如果调用一步函数,那么不用等函数中得任务执行完毕就可以执行后面的代码

//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("aa", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    NSLog(@"任务1");
});
dispatch_async(queue, ^{
    NSLog(@"任务2");
});
dispatch_async(queue, ^{
    NSLog(@"任务3");
});
NSLog(@"任务");

异步 + 并发 : 会开启新的线程(如果任务多了会开启多个线程)

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
    NSLog(@"任务1");
});
dispatch_async(queue, ^{
    NSLog(@"任务2");
});
dispatch_async(queue, ^{
    NSLog(@"任务3");
});
NSLog(@"任务");

线程间的通信

子线程回到主线程

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //执行耗时的异步操作
    NSLog(@"1");
});
dispatch_async(dispatch_get_main_queue(), ^{
   //这里使用的是dispatch_async:不用等block执行完,就会调用下面的打印代码,如果替换为使用dispatch_sync同步函数:那么会等block执行更新UI完毕,才会执行最后一句打印代码
    NSLog(@"2");
});
NSLog(@"刷新UI界面结束");

实例:  子线程下载图片,主线程刷新UI

//除主队列以外,随便写一个队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//调用异步函数
dispatch_async(queue, ^{
    //耗费时间
    UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://images.cnblogs.com/cnblogs_com/tupx/465992/o_torus_trianglestrip1.JPG"]]];
    dispatch_sync(dispatch_get_main_queue(), ^{
        //刷新主界面
        self.imageView.image = image;
    });
});

延时执行

 iOS常用延时执行,调用NSObject方法 (performSelector一旦执行号延时任务,不会卡住当前线程)

[self performSelector:@selector(run) withObject:nil afterDelay:1.0];

使用GCD函数

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
        NSLog(@"3秒后执行");
    });

NSTimer

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(startTest) userInfo:nil repeats:NO];
- (void)startTest{
     NSLog(@"---- begin-----");
}

[NSThread sleepForTimeInterval:3]; 延时执行不要使用sleepForTimeInterval (会卡住线程)

[NSThread sleepForTimeInterval:3];卡住线程3秒

一次性代码

dispatch_once可以保证函数内代码在程序运行期间值执行一次(不能当做懒加载使用)

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    NSLog(@"一次性的代码");
});

快速迭代

第一个参数: 需要遍历几次
第二个参数: 决定第三个参数的block在哪个线程中执行
第三个参数: 回掉
dispatch_apply(10, 队列queue, ^(size_tindex){
  //执行10次代码,index顺序不确定
});
//定义变量记录原始文件夹和目标文件夹的路径
NSString * sourcePatch = @"/原始文件";
NSString * destPath = @"/目标文件";

//取出原始文件中所有的文件
NSFileManager * manager = [NSFileManager defaultManager];
NSArray * files = [manager subpathsAtPath:sourcePatch];

dispatch_apply(files.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
    NSString *fileName = files[index];
    //生成原始文件的路径
    NSString *sourceFilePath = [sourcePatch stringByAppendingPathComponent:fileName];
    //生成目标文件的路径
    NSString *destFilePath = [destPath stringByAppendingPathComponent:fileName];
    //利用NSFileManager拷贝文件
    [manager moveItemAtPath:sourceFilePath toPath:destFilePath error:nil];
});

  

  

原文地址:https://www.cnblogs.com/spaceID/p/4994713.html