GCD

作者: CoderJackieYip | 来源:发表于2016-03-18 04:58 被阅读55次

    有2个核心概念

    1. 任务:执行什么操作
    2. 队列:用来存放任务

    说明:

    1. GCD封装在libdispatch库中
    2. 可以用block存放任务,也可以用函数存放任务。如下:
    /* block方式 */
    dispatch_asyc(queue, ^{...});
    
    /* function方式 */
    void function (void *data) {...};
    // &data是function的参数指针
    dispatch_asyc_f(queue, &data, function);
    

    使用步骤

    1. 定制任务、创建队列
    // 第一个参数:相当于队列名字
    // 第二个参数:队列的属性(类型),有两种类型:并发、串行
    // 注:GCD调用Create函数创建出来的变量,不需要使用Release去释放
    dispatch_queue_t queue = dispatch_queue_create(@"queueName", DISPATCH_QUEUE_CONCURRENT)```
    2. 将任务添加到队列中(GCD会自动将队列中的任务取出,放到对应线程中执行)
    

    两个执行任务的常用函数

    1. 同步:只能在当前线程中执行任务,不具备开启新线程的能力
    // queue:队列 block:任务
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    
    1. 异步:可以在新的线程中执行任务,具备开启新线程的能力,但如果放在主队列里,就不会开线程
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    

    队列类型

    1. 并发队列(Concurrent Dispatch Queue)
      可以让任务并发(同时)执行(自动开启多个线程同时执行)
      并发功能只有在异步(dispatch_async)函数下才有效
    2. 串行队列(Serial Dispatch Queue)
      让一个任务接着一个地执行(一个完毕才执行下一个)

    容易混淆的术语

    同步、异步、并发、串行

    1. 同步、异步:主要体现能否开启新线程
    • 同步:不能开线程
    • 异步:能开线程
    1. 并发、串行:任务的执行方式
    • 并发:多任务
    • 串行:单任务

    注:只有在异步函数里添加并发队列,才会并发执行多个任务。其他情况都是串行执行任务(即使开了新线程)。

    并发队列

    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    串行队列

    dispatch_get_main_queue();
    注:两种卡死的情况

    1. 当在主线程执行同步+串行主队列任务时,将会相互谦让对方,而使得无法继续执行。如下:
    -(void)viewDidLoad {
       [super viewDidLoad];
       [self syncMain];
    }
    -(void)syncMain {
       dispatch_queue_t queue = dispatch_get_main_queue();
       dispatch_sync(queue, ^{...}); //执行到这里 无法往下执行
       dispatch_sync(queue, ^{...}); 
    }
    
    1. 当在同步+串行主队列的任务中,嵌套创建同步+串行主队列任务,两个任务将会相互谦让对方,而使得无法继续执行。如下:
    > -(void)viewDidLoad {
    >    dispatch_queue_t queue = dispatch_get_main_queue;
    >    dispatch_sync{queue, ^{   //执行到这里 无法往下执行
    >       dispatch_sync{queue, ^{...}
    >                    }
    >                 }
    >      }
    > }
    

    各种队列的执行效果

    并发队列 手动创建的串行队列 主队列
    同步 - 没有开启新线程
    - 串行执行任务
    - 没有开启新线程
    - 串行执行任务
    - 没有开启新线程
    - 串行执行任务
    异步 - 开启新线程
    - 并发执行任务
    - 开启新线程
    - 串行执行任务
    - 没有开启新线程
    - 串行执行任务

    注:

    1. 以上粗体是我们使用GCD的主要组合
    2. 使用sync函数往当前串行队列中添加任务,会卡住当前的串行任务
    3. 如果异步函数的任务A里,创建了一个同步函数的任务B,则在A中,B后面的操作会等待B任务执行完,再执行。如下:
    dispatch_async(dispatch_get_global(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"先执行我")});
      NSLog(@"再执行我")
      })```
      
    
      ###其他常用函数
      > - 栅栏函数  
        ```objc
        /* 在前面的任务之行结束后它才执行,而且它后面的任务等它执行完之后才执行  
          注:这个queue不能为全局并发队列,必须是自己创建的并发队列 */
        dispatch_barrier_async(queue, ^{...});
        ```
      > - 一次性代码
        ```objc
      /*使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次*/
      // 该方法是面向成个程序的一次性代码,除了创建单例,其他情况慎用,因为该方法执行一次之后,就在整个程序运行过程中都不会执行了。(相当于该方法的代码从整个程序的代码中消失了,所以创建单例时,必须用static声明以保存单例对象)
      dispatch_once(标记指针, ^{...});
      // 使用场景:单例
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
      //只执行1次,是线程安全的});
    
    • 快速迭代
    // 第一个参数:(数量)表示迭代的次数
    // block的参数是索引
    // 说明:如果放到并发队列,在异步函数中执行时,会并发执行。
    dispacht_apply(size_t,队列,^(size_t index){});
    
    • 队列组

    把队列放到组里,组里的队列任务执行完后,执行组的notify的任务。

    // 创建组
    dispatch_group_t group = dispatch_group();
    // 注意:是dispatch_group_asyc,不是dispatch_asyc
    dispatch_group_asyc(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRORITY, 0) ^{...});
    dispatch_gourp_asyc(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRORITY, 0) ^{...});
    dispatch_group_notify(group, queue, ^{NSLog(@"执行完上面的queue任务之后,就执行我")});
    
    • GCD定时器(不受RunLoop影响)
    @interface JKYTimer
    @property (nonatomic, strong) dispatch_source_t timer;
    @end
    // dispatch_after... 是只定时一次的定时器
    // 1. 创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 2. 创建定时器(dispatch_source_t 本质是OC对象,所以必须给它加上强引用,才不会在局部方法执行完就消亡)
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    // 3. 设置定时器的各种属性(合适开始任务,每隔多长时间执行一次)
    // GCD时间参数:一般是纳秒(1秒 == 10的9次方纳秒)
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0);
    // 4. 设置回调
    dispatch_source_set_event_handler(timer, ^{...});
    // 5. 启动定时器:该定时器默认是暂停的,必须手动开启
    dispatch_resume(self.timer);
    //
    //注:
    //  1. GCD的时间变量:dispatch_time_t,如下:
      dispatch_time_t start = 2.0 * NSEC_PER_SEC;
    //  如需在该时间变量上加减时间,如加3秒,不能直接start + 3.0;而应该使用其函数设置。
    //  2. GCD的时间设置函数:dispach_time(dispatch_time_t when, int64_t delta);如下:
      dispatch_time_t start = 2.0 * NSEC_PER_SEC + dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC);
    

    相关文章

      网友评论

          本文标题:GCD

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