使用GCD

作者: 下页天 | 来源:发表于2018-10-23 17:37 被阅读5次

    介绍

    • 什么是 GCD

      全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”,纯C语言,提供了非常多强大的函数

    • GCD的优势

      GCD是苹果公司为多核的并行运算提出的解决方案

      GCD会自动利用更多的CPU内核(比如双核、四核)

      GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

      程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

    • 提示

      GCD存在于libdispatch.dylib这个库中,这个调度库包含了GCD的所有的东西,但任何IOS程序,默认就加载了这个库,在程序运行的过程中会动态的加载这个库,不需要我们手动导入。

      GCD是纯C语言的,因此我们在编写GCD相关代码的时候,面对的函数,而不是方法。

      GCD中的函数大多数都以dispatch开头。

    任务和队列

    • GCD中有2个核心概念
      • 任务:执行什么操作
      • 队列:用来存放任务
    • GCD的使用就2个步骤
      • 定制任务
      • 确定想做的事情

    将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行

    提示:任务的取出遵循队列的FIFO原则:先进先出,后进后出

    执行任务

    • GCD中有2个用来执行任务的函数

      说明:把右边的参数(任务)提交给左边的参数(队列)进行执行。

      • 用同步的方式执行任务 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); queue:队列 block:任务
      • 用异步的方式执行任务 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    • 同步和异步的区别

      • 同步:在当前线程中执行
      • 异步:在另一条线程中执行

    队列

    • 队列的类型

      • 并发队列(Concurrent Dispatch Queue)

        可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效
        - 串行队列(Serial Dispatch Queue)

        让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

    • 补充说明

    有4个术语比较容易混淆:同步、异步、并发、串行

    同步和异步决定了要不要开启新的线程

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

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

    并发和串行决定了任务的执行方式

    并发:多个任务并发(同时)执行

    串行:一个任务执行完毕后,再执行下一个任务

    • 串行队列 GCD中获得串行有2种途径

      • 使用dispatch_queue_create函数创建串行队列

        dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); // 队列名称, 队列属性,一般用NULL即可

        示例:dispatch_queue_t queue = dispatch_queue_create("wendingding", NULL); // 创建

      • 使用主队列(跟主线程相关联的队列)

        主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行,使用dispatch_get_main_queue()获得主队列

        dispatch_queue_t queue = dispatch_get_main_queue();

    • 并发队列. GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建,使用dispatch_get_global_queue函数获得全局的并发队列

      dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); // 此参数暂时无用,用0即可. 第一个参数为优先级,这里选择默认的。获取一个全局的默认优先级的并发队列。

      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列

      全局并发队列的优先级

      #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高

      #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)

      #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低

    • 各种队列的执行效果


      4.png

    block 的定义

    block 的定义有点象函数指针,差别是用 ^ 替代了函数指针的 * 号,如下所示:

    • 无参数无返回值
    //无参数,无返回值,声明和定义
    
    void(^MyBlockOne)(void) = ^(void){
    
    NSLog(@"无参数,无返回值");  
    
    };  
    MyBlockOne();//block的调用;
    
    • 有参数无返回值
    //有参数,无返回值,声明和定义
    
    void(^MyblockTwo)(int a) = ^(int a){
    
    NSLog(@"@ = %d我就是block,有参数,无返回值",a);
    
      };  
    MyblockTwo(100);
    
    • 有参数有返回值
    //有参数,有返回值
    
    int(^MyBlockThree)(int,int) = ^(int a,int b){    
    
      NSLog(@"%d我就是block,有参数,有返回值",a + b);returna + b; 
    
     };  
    MyBlockThree(12,56);
    
    • 无参数有返回值(很少用到)
    //无参数,有返回值
    
    int(^MyblockFour)(void) = ^{NSLog(@"无参数,有返回值");
            return45;
      };
    MyblockFour();
    
    • 实际开发中常用typedef 定义Block
    typedef int (^MyBlock)(int , int);
    
    @property (nonatomic,copy) MyBlock myBlockOne;
    self.myBlockOne = ^int (int ,int){
        //TODO
    }
    

    系统提供的 dispatch 方法

    为了方便地使用 GCD,苹果提供了一些方法方便我们将 block 放在主线程 或 后台线程执行,或者延后执行。

    //  后台执行:
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
         // something
    });
    
    // 主线程执行:
    dispatch_async(dispatch_get_main_queue(), ^{
         // something
    });
    
    // 一次性执行:
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // code to be executed once
    });
    
    // 延迟 2 秒执行:
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        // code to be executed on the main queue after delay
    });
    

    GCD 还有一些高级用法,例如让后台 2 个线程并行执行,然后等 2 个线程都结束后,再汇总执行结果。这个可以用 dispatch_group, dispatch_group_async 和 dispatch_group_notify 来实现. 线程分组

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
         // 并行执行的线程一
    });
    dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
         // 并行执行的线程二
    });
    dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
         // 汇总结果
    });
    

    后台运行

    使用 block 的另一个用处是可以让程序在后台较长久的运行。在以前,当 app 被按 home 键退出后,app 仅有最多 5 秒钟的时候做一些保存或清理资源的工作。但是应用可以调用 UIApplication 的beginBackgroundTaskWithExpirationHandler方法,让 app 最多有 10 分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。

    让程序在后台长久运行的示例代码如下:

    // AppDelegate.h 文件
    @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;
    // AppDelegate.m 文件
    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        [self beingBackgroundUpdateTask];
        // 在这里加上你需要长久运行的代码
        [self endBackgroundUpdateTask];
    }
    - (void)beingBackgroundUpdateTask
    {
        self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
            [self endBackgroundUpdateTask];
        }];
    }
    - (void)endBackgroundUpdateTask
    {
        [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
        self.backgroundUpdateTask = UIBackgroundTaskInvalid;
    }
    

    相关文章

      网友评论

        本文标题:使用GCD

        本文链接:https://www.haomeiwen.com/subject/vhiazftx.html