美文网首页iOS日常经验总结
GCD梳理与总结(二)常用API操练

GCD梳理与总结(二)常用API操练

作者: 牧羊的诗人 | 来源:发表于2020-08-15 14:52 被阅读0次

本章Demo

  • 延时执行(dispatch_after)
    需要注意的是:dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效。

     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         // 2秒后异步追加任务代码到主队列
         NSLog(@"----执行任务---当前线程%@",[NSThread currentThread]);
     });
    
  • 只执行一次(dispatch_once)
    通常在创建单例时使用,多线程环境下也能保证线程安全

     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         NSLog(@"----只执行一次的任务---当前线程%@",[NSThread currentThread]);
     });
    
  • 重复执行(dispatch_apply)
    快速遍历方法,可以替代for循环的函数。dispatch_apply按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。你可以把他理解成for循环遍历,其优势是可以充分利用多核的性能。

     //并发队列
     dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
    //串行队列
     dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue",   DISPATCH_QUEUE_SERIAL);
    //你可以试试并发和串行的区别
     dispatch_apply(10, globalQueue, ^(size_t index) {
         NSLog(@"执行第%zd次的任务,%@",index,[NSThread currentThread]);
         [NSThread sleepForTimeInterval:1];
     });
    
  • 队列组(dispatch_group)
    比如当我们遇到需要异步下载3张图片,都下载完之后再拼接成一个整图的时候,就需要用到gcd队列组。
    dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1
    dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。
    当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。

     //创建队列组
     dispatch_group_t group = dispatch_group_create();
     //全局串行队列
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
     dispatch_group_enter(group);
     dispatch_async(queue, ^{
         // 第一个任务
         [NSThread sleepForTimeInterval:2];
         NSLog(@"执行第一个任务");
         dispatch_group_leave(group);
     });
     
     dispatch_group_enter(group);
     dispatch_async(queue, ^{
         // 第二个任务
         [NSThread sleepForTimeInterval:2];
         NSLog(@"执行第二个任务");
         dispatch_group_leave(group);
     });
    
     dispatch_group_notify(group, dispatch_get_main_queue(), ^{
         [NSThread sleepForTimeInterval:2];
         NSLog(@"执行最后的汇总任务");
     }); 
    
  • 信号量(dispatch_semaphore)
    用来设置当前队列的最大并发数。
    信号量就3个方法,1创建信号数量,2wait,3signal。信号量就类似交通信号灯。
    dispatch_semaphore_wait判读当前信号量。为0则红灯禁止通行,等待信号量大于0后放行。放行后对信号量-1;
    dispatch_semaphore_signal与wait成对出现。表示任务已结束。对信号量+1;

    //创建信号量为2
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    //全局并发队列
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     
     //任务1
     dispatch_async(queue, ^{
         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
         NSLog(@"----开始执行第一个任务---");
         [NSThread sleepForTimeInterval:2];
         NSLog(@"----结束执行第一个任务---",); 
         dispatch_semaphore_signal(semaphore);
     });
     
     //任务2
     dispatch_async(queue, ^{
         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
         NSLog(@"----开始执行第二个任务---");
         [NSThread sleepForTimeInterval:1];
         NSLog(@"----结束执行第二个任务---");
         dispatch_semaphore_signal(semaphore);
     });
     
     //任务3
     dispatch_async(queue, ^{
         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
         NSLog(@"----开始执行第三个任务---",);
         [NSThread sleepForTimeInterval:2];
         NSLog(@"----结束执行第三个任务---");
         dispatch_semaphore_signal(semaphore);
     });
    
  • 栅栏(dispatch_barrier)
    在栅栏前放入队列的操作执行完后,再执行栅栏后放入队列的操作。当然也可以用Dispatch Group实现相同功能,只是比较而言,dispatch_barrier_async会更加顺滑。需要注意的是:使用的队列应该是你自己创建的并发队列。不可以是系统队列或者串行队列,具体原因,读者可以思考一下。

     //全局并发队列
     dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
     dispatch_async(queue, ^{
         [NSThread sleepForTimeInterval:3];
         NSLog(@"栅栏前的任务");
     });
     dispatch_async(queue, ^{
         [NSThread sleepForTimeInterval:1];
         NSLog(@"栅栏前的任务");
     });
    
     //栅栏
     dispatch_barrier_async(queue, ^{
         // 等待处理
         [NSThread sleepForTimeInterval:2];
         NSLog(@"-栅栏等待-");
     });
     
     dispatch_async(queue, ^{
         [NSThread sleepForTimeInterval:2];
         NSLog(@"栅栏后的任务");
     });
     dispatch_async(queue, ^{
         [NSThread sleepForTimeInterval:2];
         NSLog(@"栅栏后的任务");
     });
    
  • 挂起(dispatch_suspend)、恢复(dispatch_resume)队列
    简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的任务,也就是如果挂起之前已经有队列中的任务在进行中,那么该任务依然会被执行完毕

     //串行队列
     dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);
     
     dispatch_async(queue, ^{
         // 执行第一个任务
         NSLog(@"----执行第一个任务---");
     });
     
     dispatch_async(queue, ^{
         // 执行第二个任务
         NSLog(@"开始执行第二个任务");
         [NSThread sleepForTimeInterval:5];
         NSLog(@"结束执行第二个任务");
     });
     
     dispatch_async(queue, ^{
         // 执行第三个任务
         NSLog(@"开始执行第三个任务");
         [NSThread sleepForTimeInterval:5];
         NSLog(@"结束执行第三个任务");
     });
     
     //此时发现意外情况,挂起队列
     NSLog(@"suspend");
     dispatch_suspend(queue);
     
     //挂起3秒之后,恢复正常
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         //恢复队列
         NSLog(@"3秒后恢复resume");
         dispatch_resume(queue);
     });
    
  • 定时器
    GCD的定时器比NSTimer有更高的进度,而且避免了NSTimer的三大缺陷(RunLoop,不能跨越线程操作,内存泄漏风险)

      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      //创建定时器
      self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
      //构建参数。设置从何时开始/设置时间间隔
      dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, 0* NSEC_PER_SEC), 1.0 * NSEC_PER_SEC, 0);
      //要执行的时间
      dispatch_source_set_event_handler(self.timer, ^{
          NSLog(@"多久打印一次");
      });
      //运行GSD
      dispatch_resume(self.timer);
      //取消定时器
      //dispatch_cancel(self.timer); self.timer = nil;
    

总结

本节我们主要简单的梳理了GCD的一些实用API,可以发现这些API是纯c语言的,并且相关API繁多,在实际编码过程中即使有代码提示,往往编码效率还是比较慢的,造成一定的使用门槛。当然,你也通过自定义代码块来提升效率。但是我们能不能通过封装,来实现简单易用的GCD呢?下一节我们来谈一谈这个问题,CGD的封装。

相关文章

  • GCD梳理与总结(二)常用API操练

    本章Demo 延时执行(dispatch_after)需要注意的是:dispatch_after函数并不是在指定时...

  • GCD梳理与总结(二)常用API操练

    收录:原文作者牧羊的诗人本章Demo 延时执行(dispatch_after) 需要注意的是:dispatch_a...

  • GCD常用API总结

    工作中,或者是看源码的过程中总是离不开GCD的,所以总结一下方便查阅。 void dispatch_barrier...

  • swift3.0 GCD

    随着苹果推出swift3.0,很多API都发生了变化,下面我就来总结下GCD的一些常用API用法。 首先为了方便先...

  • iOS进阶-多线程-GCD

    GCD 概念 GCD队列 使用总结 实用api 实用遇到的坑 一、 概念 GCD(grand central di...

  • 多线程之GCD

    GCD,Grand Central Dispatch,iOS中常用的多线程技术,GCD提供C风格的API,相对NS...

  • 之Objective-C高级编程(GCD)

    本文主要对GCD的概念、API以及实现进行梳理. 一.CCD的概念. 1.GCD,全称是Grand Central...

  • 多线程-GCD

    GCD常用API dispatch_queue_t 队列 dispatch_async 和 dispatch_sy...

  • GCD梳理与总结(三)封装

    前面两节内容笔者主要对GCD的一些基本概念和常用API做了一个简单的梳理和归纳,对照笔者提供的Demo案例,相信读...

  • GCD常用方法

    本文主要介绍GCD一些API的使用,也就是注重实站! 一、GCD常用API1.并发队列2.串行队列3.设置自定义队...

网友评论

    本文标题:GCD梳理与总结(二)常用API操练

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