简介


  • GCD(Grand Center Dispatch)是Apple为多核的并行运算提出的解决方案,纯C语言
  • 更加适配多核处理器,且自动管理线程的生命周期,使用起来较为方便
  • GCD通过任务和队列实现多线程功能

    • 任务:描述所要执行的操作
    • 队列:用来存放所要执行的任务,队列中的任务遵循FIFO(First In First Out)原则

GCD的任务函数(是否开启新的线程)


  • 同步

    • 不具备开启新的线程的能力
    • 同步执行任务的函数

      • void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block),Block类型

        • queue:任务队列
        • block(代码块):所执行的任务
      • void dispatch_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work),函数类型(每个Block类型都对应一个函数类型)

        • queue:任务队列
        • context:传递给任务函数的参数
        • work(函数):所执行的任务
    • 同步执行任务的其他函数(barrier),在前面的任务执行完毕它才执行,它后边的任务等它执行完毕才执行

      • void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block),block类型

        • queue:任务队列,仅当该参数为并发队列时,该函数才有意义
      • dispatch_barrier_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work),函数类型

  • 异步

    • 具备开启新的线程的能力(需要将任务添加到并发队列中)
    • 异步执行任务的函数(参数意义与同步函数相同)

      • void dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
      • void dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work)
    • 异步执行任务的其他函数(barrier),在前面的任务执行完毕它才执行,它后边的任务等它执行完毕才执行(参数意义与同步函数相同)

      • void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block)
      • void dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work)

GCD的队列(任务的执行方式)


  • 并发队列

    • 开启多个线程,使队列中的多个任务并发执行(需要异步执行函数的配合)
  • 串行队列

    • 队列中的任务一个接一个顺序地执行
  • 队列的种类

    • 串行队列

      • dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)

        • label:通常为0
        • attr:队列类型,DISPATCH_QUEUE_SERIAL
    • 并发队列

      • dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)

        • label:通常为0
        • attr:队列类型 DISPATCH_QUEUE_CONCURRENT
    • 主队列(串行,只能在主线程中运行)

      • dispatch_queue_t dispatch_get_main_queue(void),获取主队列
    • 全局队列(并发)

      • dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags)

任务与队列的组合


  • 同步函数

    • 同步函数主队列

      /**
      - 运行在主线程主队列(未开启新的线程),主线程被卡死
      - 原因:任务代码等待着当前函数执行完毕才能执行(当前函数正在执行且未执行完毕);
      当前函数等待着任务代码 执行完毕才能执行(当前任务正在执行且未执行完毕);
      相互等待,出现死锁
      */ //获取主队列
      dispatch_queue_t queue = dispatch_get_main_queue();
      //添加任务到队列
      dispatch_sync(queue, ^{
      //任务1代码
      });
      dispatch_sync(queue, ^{
      //任务2代码
      });
    • 同步函数串行队列

      /**
      - 运行在主线程串行非主队列(未开启新的线程),任务串行执行
      */ //创建串行队列
      dispatch_queue_t queue = dispatch_queue_create("com.23565@qq", DISPATCH_QUEUE_SERIAL);
      //添加任务到队列
      dispatch_sync(queue, ^{
      //任务1代码
      });
      dispatch_sync(queue, ^{
      //任务2代码
      });
    • 同步函数并发队列

      /**
      - 运行在非主线程并发队列(未开启新的线程),任务串行执行
      */ //获取全局队列(并发)
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      //将任务加至队列
      dispatch_sync(queue, ^{
      //任务1代码
      });
      dispatch_sync(queue, ^{
      //任务2代码
      });
  • 异步函数

    • 异步函数主队列

      /**
      - 运行在主线程主队列(未开启新的线程),任务串行执行
      */ // 获得主队列
      dispatch_queue_t queue = dispatch_get_main_queue();
      // 将任务加入队列
      dispatch_async(queue, ^{
      //任务1代码
      });
      dispatch_async(queue, ^{
      //任务2代码
      });
    • 异步函数串行队列

      /**
      - 运行在主函数串行非主队列(未开启新的线程),任务串行执行
      */ //创建串行队列
      dispatch_queue_t queue = dispatch_queue_create("com.23565@qq", DISPATCH_QUEUE_SERIAL);
      //将任务加至队列
      dispatch_async(queue, ^{
      //任务1代码
      })
      dispatch_async(queue, ^{
      //任务2代码
      })
    • 异步函数并发队列

      /**
      - 运行在非主线程并发队列(开启新的线程),任务并发执行
      - 系统根据任务创建线程(无法确定任务执行在哪个线程)
      */ //获得全局并发队列
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      //将任务加入队列
      dispatch_async(queue, ^{
      //任务1代码
      });
      dispatch_async(queue, ^{
      //任务2代码
      });

      线程之间的通信


  • 从主线程到子线程

    • 注意

      • 只有异步函数与并发队列的组合,才会开启新的线程,使任务并发执行
      • 通常使用异步函数将任务添加到并发队列中,来实现从主线程到子线程的通信
    • 实现代码

      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      //需要在子线程中执行的任务代码
      })
  • 从子线程到主线程

    • 注意

      • 主队列中的任务只能在主线程中执行
      • 通常使用异步/同步函数将任务添加到主队列中,来实现从子线程到主线程的通信
    • 实现代码

      dispatch_async(dispatch_get_main_queue(), ^{
      //需要在主线程中执行的代码
      })

GCD的其他任务


  • 单次执行(通常用在单例模式的设计中)

    //定义一个标记
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    //此处的代码只会被执行一次
    });
  • 延迟执行

    /**
    - 方法一(GCD)
    */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //此处的代码将延迟执行
    }); /**
    - 方法二(performSelector)
    */
    [self performSelector:@selector(run) withObject:self afterDelay:2.0]; /**
    - 方法三(NSTimer)
    */
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO]
  • 快速迭代

    void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t))
    /**
    iterations:迭代执行的次数
    queue:任务队列
    block:迭代执行的代码
    size_t:用来定义当前迭代到第几次,需要自己添加,如在size_t后添加index索引,记录当前的迭代次数
    */
  • Barrier

    /**
    - Barrier中的任务,只能在它前面的任务执行完毕才能执行
    Barrier后的任务,只能等到它执行完毕才能执行
    - 要将队列添加到自己创建的并发队列中,否其功能等同于函数
    void dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
    */
    //创建队列(通常是自己创建的并发队列)
    dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_CONCURRENT);
    //将任务添加到队列
    dispatch_async(queue, ^{
    //在Barrier前执行的任务代码
    });
    dispatch_barrier_async(queue, ^{
    //Barrier中的任务代码
    });
    dispatch_async(queue, ^{
    //在Barrier后执行的任务代码
    });
  • 队列组

    //获取全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //创建队列组
    dispatch_group_t group = dispatch_group_create();
    //添加任务到队列组
    dispatch_group_async(group, queue, ^{
    //任务1代码
    });
    dispatch_group_async(group, queue, ^{
    //任务2代码
    });
    dispatch_group_notify(group, queue, ^{
    //任务3代码
    /**
    group组中的所有任务执行完毕在执行
    若group为空,则立即执行
    */
    });

GCD定时器


  • 实现原理

    • 创建一个DISPATCH_SOURCE_TYPE_TIMER类型的dispatch source,并添加到dispatch queue,通过dispatch source来响应事件
    • 通过函数void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway),来设置dispatch source的执行事件
  • 实现代码

    //获得队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    //创建一个定时器
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); //设置定时器的各种属性(起止时间)
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8.0 * NSEC_PER_SEC));
    uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
    //设置
    dispatch_source_set_timer(self.timer, start, interval, 0); //设置回调
    dispatch_source_set_event_handler(self.timer, ^{
    //定时器被触发时所要执行的代码
    }); //开启定时器
    dispatch_resume(self.timer);
    //取消定时器
    dispatch_cancel(self.timer);
 
 

OC - 19.GCD的更多相关文章

  1. OC - 19.pthread和NSThread

    简介 恰当的使用多线程编程可以提供任务的执行效率和系统资源的利用率 多线程是为了提高资源利用率,和应用程序的响应速度,多个线程共享应用资源 每个应用程序都有一个主线程,通常用来做UI界面刷新等 比较耗 ...

  2. JSPatch学习笔记

    本文参考JSPatch wiki :https://github.com/bang590/JSPatch/wiki 1.概念 JSPatch是一个轻量的JS引擎,能够使用JavaScript语言来调用 ...

  3. IOS 多线程04-GCD详解 底层并发 API

    注:本人是翻译过来,并且加上本人的一点见解. 前言 想要揭示出表面之下深层次的一些可利用的方面.这些底层的 API 提供了大量的灵活性,随之而来的是大量的复杂度和更多的责任.在我们的文章常见的后台实践 ...

  4. IOS线程的一些总结

    主线程的作用 (在主线程中才能设置) 显示/刷新UI界面 处理UI事件(比如点击事件.滚动事件.拖拽事件): 主线程的使用注意 别将比较耗时的操作放到主线程中. 耗时操作会卡住主线程.影响体验. [N ...

  5. 从Objective-C到Swift 单例模式

    在Objective-C中经常会用到单例模式.最常见的就是: [UIApplication sharedApplication].delegate 这里的sharedApplication就是一个返回 ...

  6. ZeroC ICE的远程调用框架 Callback(一)-AMI异步方法调用框架

    Ice框架提供了不少回调设施,其中一些是使用Ice远程调用进行ami模式或amd模式的支撑.本篇来看一下用于代理端的回调设施. Ice代码中有好几个Callback相关命名的基类,并且slice还会为 ...

  7. Hdu1695 GCD 2017-06-27 22:19 30人阅读 评论(0) 收藏

    GCD Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  8. 网络与多线程---OC中多线程方法GCD(二)

    小编在前一篇中介绍了多线程实现的五种常用方法.在接下来所介绍的这种方法是最具有魅力的,最具有诱惑的实现多线程的方案---GCD 一.什么是GCD GCD是Grand Central Dispatch的 ...

  9. OC开发_整理笔记——多线程之GCD

    一.进程和线程   二.各种队列! 1.GCD:Grand Central Dispatch 2.串行队列(Serial)      你可以创建任意个数的串行队列,每个队列依次执行添加的任务,一个队列 ...

随机推荐

  1. Spring MVC 解读——View,ViewResolver(转)

    上一篇文章(1)(2)分析了Spring是如何调用和执行控制器方法,以及处理返回结果的,现在我们就分析下Spring如何解析返回的结果生成响应的视图. 一.概念理解 View ---View接口表示一 ...

  2. crontab(linux下定时执行任务命令)

    在linux在可以通过在脚本里(列如sh)写如日常需要进行的操作,然后通过crontab定时运行脚本. Linux下的任务调度分为两类,系统任务调度和用户任务调度. 系统任务调度:系统周期性所要执行的 ...

  3. 51Testing招聘软件测试课程研发人员

    最近有些两三年测试工作经验的小伙伴对自己的下一个工作有些迷茫,感觉很难有技术的突破,毕竟公司不是学校,不会允许员工海阔天空的去尝试各种新的技术.现在我就送给这些好学上进的小伙伴一个礼物,51Testi ...

  4. Linux学习笔记17——输入输出重定向

    1 何谓输入输出重定向? 默认情况下输入是由键盘输入的.输出是默认的输出到屏幕上.而输入输出重定向就是改变默认的输入输出方向 2 freopen函数 freopen是被包含与stdio.h头文件中,是 ...

  5. Oracle LOB

    Oracle LOB Oracle .NET Framework 数据提供程序包括 OracleLob 类,该类用于使用 Oracle LOB 数据类型. OracleLob 可能是下列 Oracle ...

  6. centos 6.5上部署jetty

    和tomcat是一样的,在部署容器之前,我们首先得有java环境 这里我们选择用rpm包的方式安装jdk 这里我们上传我之前下载好的jdk包 然后按照该文章http://blog.csdn.net/x ...

  7. c#语言基础编程—string

    引言 在c#中经常会有相关的string的操作,string类型为引用类型,集成于Object,所以会有四个方法.详情可见 值类型和引用类型的区别 里面详细介绍了,值类型和引用类型的区别和应用场合,所 ...

  8. Windows 10 Technical Preview 10041 使用 IIS Express 运行网站应用程序异常

    在 Windows 10 中使用 Visual Studio 2013 Ultimate with Update 4 开发网站,9926 的时候还好好的,升到 10041 就不能调试了: “Syste ...

  9. CSS3动画变形Animations

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. [JS][jQuery]remove()与 empty()的差别

    要用到移除指定元素的时候,发现empty()与remove([expr])都能够用来实现.可细致观察效果的话就能够发现. empty()是仅仅移除了 指定元素中的全部子节点.拿$("p&qu ...