• iOS多线程-02-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("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);
    	```
  • 相关阅读:
    在Linux上使用C语言编程获取IPv4地址及子网掩码
    使用gdb进行写操作
    [中英对照]The Art Of Reporting Bugs | 报bug的艺术
    [中英对照]Introduction to Remote Direct Memory Access (RDMA) | RDMA概述
    Intel万兆网卡背靠背连接ping不通那点事儿
    [中英对照]The sysfs Filesystem | sysfs文件系统
    图说单播,组播,广播,选播和地域播
    Ubuntu双网卡不双待攻略
    反汇编容易反编译难
    PHP之路——微信公众号授权获取用户信息
  • 原文地址:https://www.cnblogs.com/theDesertIslandOutOfTheWorld/p/4759207.html
Copyright © 2020-2023  润新知