zoukankan      html  css  js  c++  java
  • 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];
    });
    

      

      

  • 相关阅读:
    聪明人 & 普通人
    13种模型及方法论
    面向大规模商业系统的数据库设计和实践
    分治算法
    软件架构
    隐含前提思维模型
    Git回滚代码到某个commit
    使用arthas排查 test 问题
    Arthas
    docker 操作入门
  • 原文地址:https://www.cnblogs.com/spaceID/p/4994713.html
Copyright © 2011-2022 走看看