• iOS多线程开发之GCD


    GCD简介

    GCDGrand Central Dispatch的缩写,是基于C语言。是苹果公司为多核的的并行运算提出的解决方案。GCD负责创建线程和调度需要执行的任务,线程是由系统管理的。

    开发中经常使用到GCD里的知识例如:

    • 创建单例的时候:
    static dispatch_once_t onceToken;
       dispatch_once(&onceToken, ^{
           //code
       });
    

    这个让code只执行一次。

    • 获取主线程
    dispatch_async(dispatch_get_main_queue(), ^{
            //code
        });
    
    • 异步执行
    dispatch_async(dispatch_queue_create("robertlee", DISPATCH_QUEUE_SERIAL), ^{
            //code
        });
    

    GCD能做的事远不止这些,接下来让我们了解一下GCD。GCD有两个核心概念,分别是任务和队列,接下来让我们介绍一下。

    队列和任务

    任务

    任务也就是用户提交给队列的工作单元,也就是代码块,在GCD中需要是以block的形式存在。

    队列

    Dispatch Queue(队列) 是用来存放任务的集合,负责管理开发者提交的任务。队列严格遵循FIFO(先进先出)的原则,队列会维护和使用一个线程池来处理用户提交的任务,线程池来执行队列管理的任务。

    队列主要分两种:

    • 串行队列(Serial Dispatch Queue):
      串行队列中的线程池中只有一个线程,每次只能执行一个任务,前一个任务执行完,才能够执行下一个任务。

    • 并行队列(Concurrent Dispatch Queue):
      并行队列的线程池提供了多个线程,可以多个任务同时执行。

    队列的创建

    //表示队列
    dispatch_queue_t
    //创建队列,第一个参数表示队列的字符串,第二个控制创建队列的类型,
    //若第二个参数为NULL,则表示创建的队列为串行队列
    dispatch_queue_create(const char *_Nullable label,
            dispatch_queue_attr_t _Nullable attr);
    
    • 创建串行队列
    //创建串行队列:
    dispatch_queue_t syncqueue = dispatch_queue_create("robertlee.testqueue", DISPATCH_QUEUE_SERIAL);
    
    • 创建并行队列
    //创建并行队列
    dispatch_queue_t casyncqueue = dispatch_queue_create("robertleel.testqueue", DISPATCH_QUEUE_CONCURRENT);
    
    • 获取全局并发队列

    全局并发队列可以同时并行的执行多个任务,系统提供了多个并发的队列,整个应用内共享,可以使用dispatch_get_global_queue()函数获取这些队列

    dispatch_queue_global_t
    //第一个参数用于指定队列的优先级, 第二个参数为以后做准备,填0即可。
    dispatch_get_global_queue(long identifier, unsigned long flags);
    
    //全局并发队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    

    系统宏定义了几个优先级:

    #define DISPATCH_QUEUE_PRIORITY_HIGH 2  //高
    #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0  //中 默认
    #define DISPATCH_QUEUE_PRIORITY_LOW (-2)   //底
    #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN //后台
    
    • 获取主队列

    GCD自带了一个串行队列,就是主队列,只要提交到主队列中的任务,都会在主线程中执行。通过dispatch_get_main_queue()函数获得主队列

    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    

    提交任务

    上面已经介绍了队列的创建,接下来让我们看看如何提交任务。

    • 同步提交任务
    dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
    
    dispatch_sync_f(dispatch_queue_t queue,
            void *_Nullable context, dispatch_function_t work);
    

    GCD提供了两种方法进行同步提交任务,第一种方法是将代码块以同步的方式提交给queue队列。第一个参数是目标队列,第二个参数为代码块。
    第二种方式是将函数以同步的方式提交给队列,还包括了content 上下文。

    • 异步的方式提交任务

      dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
      dispatch_async_f(dispatch_queue_t queue,

      void *_Nullable context, dispatch_function_t work);
      

    同步异步的方法名只差一个a字母,一个以同步方式执行任务,一个以异步的方式执行任务,后者在开发中最常用。
    大致用法如下:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_P 大专栏  iOS多线程开发之GCDRIORITY_DEFAULT, 0), ^(void) {
                    //code
    
                    dispatch_async(dispatch_get_main_queue(), ^(void) {
                        //code
                    });
                });
    

    先异步执行代码块,在执行完之后,可能会刷新页面、修改UI等步骤,需要到主线程中执行。

    上面我们说了有3种创建队列的方式,又有两种队列执行方式,所以按照正常情况下会有6中组合方式。

    • 1、同步(sync) + 并行 //没开启新线程 串行执行任务
    • 2、同步(sync) + 串行 // 没开启新线程 串行执行任务
    • 3、同步(sync) + 主队列 //导致死锁 引发程序奔溃
    • 4、异步(async) + 并行 // 开启新线程、并行执行任务
    • 5、异步(async) + 串行 // 开启新线程,串行执行任务
    • 6、异步(async) + 主队列 // 没开启新线程 串行执行任务

    总结:同步异步觉得是否开启新线程,并发串行决定任务的执行方式。

    其他常用方法

    单次执行任务 dispatch_once()

    dispatch_once(dispatch_once_t *predicate,
            DISPATCH_NOESCAPE dispatch_block_t block);
    

    刚开始已经提到了单次执行方法。第一个参数表示dispatch_once_t类型的指针,第二个参数是代码块,在创建代理的时候经常用到。

    多次重复执行任务 dispatch_apply()

    GCD中可以多次执行相同代码,使用dispatch_apply()函数,第一个参数表示次数,第二个参数表示执行队列,第三个参数表示代码块。

    void dispatch_apply(size_t iterations,
            dispatch_queue_t DISPATCH_APPLY_QUEUE_ARG_NULLABILITY queue,
            DISPATCH_NOESCAPE void (^block)(size_t));
    

    延时方法 dispatch_after()

    dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
            dispatch_block_t block);
    

    延时方法,代码块在延时之后才开始执行:

    • 第一个参数表示延时时间
    • 第二个参数表示延迟完执行任务的队列
    • 第三个参数表示代码块

    GCD 的队列组:dispatch_group

    有时候我们在请求页面数据的时候,可能需要请求2个接口或者多个,我们希望所有请求完之后再刷新界面,这个时候如何保证接口都请求完成呢,这就是GCD队列组的用处了。

    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"0");
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1");
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2");
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"3");
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"4");
    });
    
    dispatch_group_leave(group);
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"end");
    });
    

    也可以使用dispatch_semaphore_tdispatch_group_wait实现,但是我们后面会介绍这个方法。

    GCD 信号量 dispatch_semaphore

    信号量在使用线程同步的时候经常用到,dispatch_semaphore有三种方法可以使用

    dispatch_semaphore_create:初始化一个信号量
    dispatch_semaphore_signal:发送一个信号,让信号总量加1
    dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。

    在YYCache中如何使用dispatch_semaphore来给线程加锁:

    YYDiskCache中使用的线程锁为:定义了一个信号量:dispatch_semaphore_t _lock;

    #define Lock() dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER)
    #define Unlock() dispatch_semaphore_signal(self->_lock)
    

    这就是GCD,有很多未使用的功能都没有介绍,下次遇到再补充。

    感谢:

    iOS多线程:『GCD』详尽总结

  • 相关阅读:
    Add two numbers
    House Robber && House Robber II
    Clone Graph
    224. Basic Calculator
    29. Divide Two Integers
    365. Water and Jug Problem
    435. Non-overlapping Intervals
    452. Minimum Number of Arrows to Burst Balloons
    138. Copy List with Random Pointer
    43. Multiply Strings
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12433210.html
Copyright © 2020-2023  润新知