OC - 19.GCD

简介


  • 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("[email protected]", 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("[email protected]", 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);
时间: 2024-12-07 17:44:47

OC - 19.GCD的相关文章

iOS开发——多线程OC篇&GCD实用总结

GCD实用总结 图片下载 注:iOS开发中常见GCD的实用也就这些了, 先来看看之前我们经常使用的方式: 1 static NSOperationQueue * queue; 2 3 - (IBAction)someClick:(id)sender { 4 self.indicator.hidden = NO; 5 [self.indicator startAnimating]; 6 queue = [[NSOperationQueue alloc] init]; 7 NSInvocationO

彻底搞懂OC中GCD导致死锁的原因和解决方案

GCD提供了功能强大的任务和队列控制功能,相比于NSOperationQueue更加底层,因此如果不注意也会导致死锁. 所谓死锁,通常指有两个线程A和B都卡住了,并等待对方完成某些操作.A不能完成是因为它在等待B完成.但B也不能完成,因为它在等待A完成.于是大家都完不成,就导致了死锁(DeadLock). 有一定GCD使用经验的新手通常认为,死锁是很高端的操作系统层面的问题,离我很远,一般不会遇上.其实这种想法是非常错误的,因为只要简单三行代码(如果愿意,甚至写在一行就可以)就可以人为创造出死锁

OC - 19.pthread和NSThread

简介 恰当的使用多线程编程可以提供任务的执行效率和系统资源的利用率 多线程是为了提高资源利用率,和应用程序的响应速度,多个线程共享应用资源 每个应用程序都有一个主线程,通常用来做UI界面刷新等 比较耗时的任务如果放在主线程中,可能会造成主线程的堵塞,无法响应用户操作,通常为耗时任务创建自己的线程,与主线程并发执行 多线程编程在一定程度上提高了系统资源的利用率和任务处理速度,但是线程不易过多,否则会引发以下问题. 过多的线程会造成处理机的频繁调度,线程调度需要消耗大量的系统资源. 同一进程下的多个

[蓝桥杯] 最大比例

峰值内存消耗 < 256M CPU消耗  < 3000ms [题目描述 - Problem Description] X星球的某个大奖赛设了M级奖励.每个级别的奖金是一个正整数. 并且,相邻的两个级别间的比例是个固定值. 也就是说:所有级别的奖金数构成了一个等比数列. 比如: 16,24,36,54 其等比值为:3/2 现在,我们随机调查了一些获奖者的奖金数. 请你据此推算可能的最大的等比值. 输入格式: 第一行为数字N(N<=100),表示接下的一行包含N个正整数 第二行N个正整数Xi

谈谈多线程开发中的线程和任务的理念

前段时间写了一个iOS端的数据统计SDK,数据统计有些复杂的计算和数据上报操作.由于有些操作比較耗时.所以不得不在后台线程进行操作,由此引发了我对多线程的思考,在iOS开发中,一般非常难再见到直接使用NSThread进行多线程编程的了.由于苹果提供了另外几种多线程开发的解决方式.而这些解决方式面向的不再是线程,而是面向的是任务,以下就来以iOS和Android为例简单谈谈的线程和任务的相关概念. 线程: 我们在学习基础的编程语言的时候,肯定听说过线程的概念,线程是操作系统调度的最小单位,共享进程

Dispatch Queues调度队列

前言-死锁案例 // 在主线程中执行 dispatch_queue_t queueMain = dispatch_get_main_queue(); dispatch_sync(queueMain, ^{ NSLog(@"+++++++"); }); NSLog(@"hahahaha"); 案例分析:运行结果是程序阻塞在dispatch_sync()处.由于main线程执行到dispatch_sync()处,线程处于等待状态.将block任务块添加到主串行队列最后,

读者的梦想,图书馆员的梦魇

作者:鱼鸟兽 链接:https://zhuanlan.zhihu.com/p/21602568 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 一本无法被数字化的书 <S.>包含了两个世界:印刷的世界和书写的世界.在印刷的世界,也就是由旧书<忒修斯之船>及其所构成的故事里,充满了各种谜题:失忆的主角.被禁言的船员.神秘的女子,还有故事的创作者--作家石察卡和他的译注者柯岱拉.所有的元素之间有什么联系?<忒修斯之船>并没有给我们答案. 珍

istView的项移除

如标题所言,是做删除ListView绑定项的功能的:鉴于这个功能当时确实花费了很多时间,并且网上也找不到删除所需的案例,所以,我就做了一份案例,仅供各位前辈和同行进行参考,如有不当之处,还望指点,我将再接再励,下面进入正题: 按照需求我们是需要实现的功能:点击删除的时候,把整个Items给移除,最初用listview.Items.RemoveAt(Listview.SelectedIndex)这样的方式来删除,调试的时候报"灾难性......"的错误,无奈我只能尝试listview.I

Html5 绘制旋转的太极图

采用Html5+JavaScript在Canvas中绘制旋转的太极图,如下图所示: 具体思路和绘制逻辑,在上图中已有说明,代码如下: 1 <script type="text/javascript"> 2 3 //只画边框线,无填充 4 function bigCircle(ctx,x, y, r, st, end, w,oc) { 5 ctx.lineWidth = w; 6 ctx.beginPath(); 7 ctx.arc(x, y, r, st, end, oc)