美文网首页
iOS GCD之函数与队列

iOS GCD之函数与队列

作者: 木扬音 | 来源:发表于2021-06-09 20:38 被阅读0次

    GCD全称Grand Central Dispatch,基于C语言的函数,会自动利用更多的CPU内核自动管理线程生命周期
    总结:GCD就是将任务添加到队列,并指定任务执行的函数

    函数

    GCD中有两种执行任务的方式:同步函数(dispatch_sync)异步函数(dispatch_async)

    同步函数(dispatch_sync)

    • 必须等待当前语句执行完毕,才会执行下一条语句,会阻塞当前线程
    • 不会开辟新的线程

    异步函数(dispatch_async)

    • 不必等待当前语句执行完毕,就可以执行下一条语句
    • 具有开辟新线程的能力,但不一定会开辟新线程,与当前任务所指定的队列类型相关

    队列

    队列(dispatch queue)是一种数据结构,特殊的线性表,是用来存放任务的队列,遵循FIFO(先进先出)原则,新任务总是被插入到队尾,任务从队首开始读取,每读取一个任务,该任务就会从队列总释放,

    队列

    串行队列

    • 同一时刻只能执行一个任务
    • dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);创建串行队列
    • DISPATCH_QUEUE_SERIAL也可以用NULL代替
    // 串行队列的获取方法
    dispatch_queue_t serialQueue1 = dispatch_queue_create("com.CJL.Queue", NULL);
        dispatch_queue_t serialQueue2 = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_SERIAL);
    
    串行队列

    并发队列

    • 同一时刻可以执行多个任务
    • dispatch_queue_create("xxx", DISPATCH_QUEUE_CONCURRENT);创建并发队列
    • 只有在异步函数下才有并发效果
    // 并发队列的获取方法
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
    
    并发队列

    主队列

    主队列(Main Dispatch Queue)

    • 特殊的串行队列
    • 专门用来在主线程上调度任务的串行队列,依赖于主程序、主Runloop,在main函数之前调用
    • dispatch_get_main_queue()获取主队列
    //主队列的获取方法
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    

    全局队列

    全局队列(Global Dispatch Queue)

    • GCD默认的并发队列
    • dispatch_get_global_queue(intptr_t identifier, uintptr_t flags);获取全局并发队列
      • 第一个参数表示队列优先级,默认DISPATCH_QUEUE_PRIORITY_DEFAULT=0,在iOS9.0后被服务质量quality-of-service取代
      • 第二个参数使用0
    //全局并发队列的获取方法
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    
    //优先级从高到低(对应的服务质量)依次为
    - DISPATCH_QUEUE_PRIORITY_HIGH       -- QOS_CLASS_USER_INITIATED
    - DISPATCH_QUEUE_PRIORITY_DEFAULT    -- QOS_CLASS_DEFAULT
    - DISPATCH_QUEUE_PRIORITY_LOW        -- QOS_CLASS_UTILITY
    - DISPATCH_QUEUE_PRIORITY_BACKGROUND -- QOS_CLASS_BACKGROUND
    

    在日常开发中,全局队列+并发并列一般是这样配合使用的

    //主队列 + 全局并发队列的日常使用
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //执行耗时操作
        dispatch_async(dispatch_get_main_queue(), ^{
            //回到主线程进行UI操作
        });
     });
    

    函数与队列的不同组合

    串行队列 + 同步函数

    不会开辟线程,任务一个一个按顺序执行

    串行队列 + 同步函数

    串行队列 + 异步函数

    开辟线程,任务一个一个按顺序执行

    串行队列 + 异步函数

    并行队列 + 同步函数

    不开辟新线程,任务一个一个按顺序执行

    并行队列 + 同步函数

    并行队列 + 异步函数

    开辟新线程,任务一起乱序执行

    并行队列 + 异步函数

    总结

    队列+函数

    相关面试题

    【面试题1】异步函数+并发队列

    - (void)interview01{
        //并行队列
        dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        // 耗时
        dispatch_async(queue, ^{
            NSLog(@"2");
            dispatch_async(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }
    ----------打印结果-----------
    输出顺序为:1 5 2 4 3
    

    异步函数会开辟新线程,不会阻塞主队列,

    分析
    • 主队列的任务是NSLog(1)、异步Block、NSLog(5),因为NSLog(1)NSLog(5)的复杂度是一样的,而异步Block的复杂度更高,所以NSLog(1)和NSLog(5)优先于异步Block
    • 异步Block中同理,NSLog(2)和NSLog(4)优先于异步Block
    • 主线程阻塞或其他极端情况下,NSLog(2)有可能优先NSLog(1)NSLog(5)

    【面试题2】异步函数嵌套 同步函数+并发队列

    - (void)interview02{
        //并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        //异步函数
        dispatch_async(queue, ^{
            NSLog(@"2");
            //同步函数
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }
    
    ----------打印结果-----------
    输出顺序为:1 5 2 3 4
    
    分析
    • 任务1和任务5的分析和前面面试一样,所以执行顺序是任务1、任务5、异步block
    • 在异步block中首先执行任务2,因为同步函数会阻塞线程,所以执行顺序是任务2、任务3、任务4

    【面试题3】异步串行嵌套同步串行

    - (void)interview03{
        // 串行队列
        dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", NULL);
        NSLog(@"1");
        // 异步函数
        dispatch_async(queue, ^{
            NSLog(@"2");
            // 同步函数
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }
    
    ----------打印结果-----------
    输出顺序为:1 5 2 死锁崩溃
    
    分析
    • 任务1、任务5、异步block的执行顺序和上面分析的一样
    • 异步block中,首先将任务2同步block任务4添加到串行队列,等待执行,串行队列中的任务顺序任务2 --> 同步block --> 任务4
    • 开始执行时,先执行任务2,再执行同步Block,因为是同一个串行队列,所以会将任务3添加在任务4的后面,串行队列中的任务顺序同步block --> 任务4 -->任务3
    • 因为同步函数会阻塞当前线程,所以任务4等待同步block执行完毕,但是当前又是串行队列,遵循先进先出原则,所以任务3等待任务4,造成死锁
    • 死锁会有一个关键的堆栈信息_dispatch_sync_f_slow

    【面试4】异步函数+同步函数+并发队列

    下面代码的执行顺序是什么?(答案是 AC)
    A: 1230789
    B: 1237890
    C: 3120798
    D: 2137890

    - (void)interview04{
        //并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_async(queue, ^{ // 耗时
            NSLog(@"1");
        });
        dispatch_async(queue, ^{
            NSLog(@"2");
        });
        
        // 同步
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        
        NSLog(@"0");
    
        dispatch_async(queue, ^{
            NSLog(@"7");
        });
        dispatch_async(queue, ^{
            NSLog(@"8");
        });
        dispatch_async(queue, ^{
            NSLog(@"9");
        });
    }
    
    ----------打印结果-----------
    输出顺序为:(1 2 3 无序)0(7 8 9 无序),可以确定的是 0 一定在3之后,在789之前
    
    分析
    • 因为任务1、任务2是异步并发,会开辟新线程,所以没有固定顺序,
    • 同理,任务7、任务8、任务9也没有固定顺序
    • 因为任务3是同步并发,会阻塞当前线程,所以任务3任务0之前执行,所以任务0会在任务3之后,任务7、8、9之前

    【面试题5】下面队列有几种类型

    /串行队列 - Serial Dispatch Queue
    dispatch_queue_t serialQueue = dispatch_queue_create("com.CJL.Queue", NULL);
        
    //并发队列 - Concurrent Dispatch Queue
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
        
    //主队列 - Main Dispatch Queue
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
    //全局并发队列 - Global Dispatch Queue
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    

    队列只有两种:

    • 并发队列:并发队列(DISPATCH_QUEUE_CONCURRENT)全局并发队列(dispatch_get_global_queue)
    • 串行队列:串行队列(DISPATCH_QUEUE_SERIAL \ NULL)串行主队列(dispatch_get_main_queue)

    相关文章

      网友评论

          本文标题:iOS GCD之函数与队列

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