美文网首页
OC底层原理 - 20 GCD之应用篇

OC底层原理 - 20 GCD之应用篇

作者: 卡布奇诺_95d2 | 来源:发表于2021-04-02 16:05 被阅读0次

    GCD

    简介

    • GCD全称是Grand Central Dispatch,由C语言开发的。
    • GCD是苹果公司为多核的并行运算提出的解决方案
    • GCD会自动利用更多的CPU内核(比如双核、四核)。
    • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)。因此在使用GCD的时候,只需要管理执行的任务,而不需要管理线程。

    GCD核心

    GCD的核心分为三步骤:

    1. 创建任务
    2. 创建队列
    3. 将任务添加到队列中,并指定执行任务的函数

    根据核心三步骤,写一个简单的GCD代码

    //1. 创建任务
    dispatch_block_t block = ^{
        NSLog(@"hello GCD");
    };
    //2. 创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL);
    
    //3. 将任务添加到队列中,并指定执行方式,此处为异步执行
    dispatch_async(queue, block);
    
    //上面三个步骤的代码可以合并成以下形式
    dispatch_async(dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL), {
        NSLog(@"hello GCD");
    });
    

    注意:

    • 任务需要使用block进行封装
    • 任务的block没有参数,也没有返回值

    函数与队列

    函数

    在GCD中,执行任务的方式有两种:同步执行和异步执行。

    • 同步执行

      • 使用函数dispatch_sync
      • 必须等待当前语句执行完毕,才会执行下一条语句
      • 不会开启线程,即不具备开启新线程的能力
      • 由于不会开启新线程,因此block任务是在当前线程中执行
    • 异步执行

      • 使用函数dispatch_async
      • 不用等待当前语句执行完毕,就可以执行下一条语句
      • 会开启线程执行block任务,即具备开启新线程的能力(注意:不表示每一次异步执行都会开新线程)

    队列

    多线程中所说的队列(Dispatch Queue)是指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,遵循先进先出(FIFO)原则,即新任务总是被插入到队尾,而任务的读取从队首开始读取。每执行完一个任务,则任务队列中释放一个任务,如下图所示:

    队列.png

    在GCD中,队列主要分为串行队列(Serial Dispatch Queue)并发队列(Concurrent Dispatch Queue)两种,如下图所示

    image.png
    • 串行队列
      • 每次只有一个任务被执行,等待上一个任务执行完毕再执行下一个

      • 创建串行队列的方式如下:

        dispatch_queue_t serialQueue1 = dispatch_queue_create("com.HQ.Queue", NULL);
        dispatch_queue_t serialQueue2 = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL);
        
    • 并发队列
      • 一次可以并发执行多个任务,即开启多个线程,并同时执行任务

      • 创建并发队列的方式如下:

        dispatch_queue_t concurrentQueue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
        

    在GCD中,提供了两种特殊的队列:主队列(Main Dispatch Queue)全局并发队列(Global Dispatch Queue)

    • 主队列

      • 主线程上用于调度任务的串行队列,依赖于主线程、主Runloop,在main函数调用之前自动创建,通常UI操作必须使用主队列
      • 不会开启线程
      • 如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度
      • 使用dispatch_get_main_queue()获得主队列
    • 全局并发队列

      • GCD提供的默认的并发队列,使用多线程开发时,如果对队列没有特殊需求,在执行异步任务时,可以直接使用全局并发队列
      • 使用dispatch_get_global_queue函数获取全局并发队列
      //全局并发队列的获取方法
      //参数1:队列优先级
      //参数2:保留参数
      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
      

    函数与队列的不同组合

    • 串行队列 + 同步函数:任务是一个接着一个完成,不会开辟新的线程
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
        dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL);
        for(int i = 0; i<5; i++){
            dispatch_sync(queue, ^{
                NSLog(@"串行队列 + 同步执行 + %d", i);
                NSLog(@"%@", [NSThread currentThread]);
            });
        }
        NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
    }
    
    //执行结果如下
    2021-04-02 14:30:09.694649+0800 001---函数与队列[79317:12540708] [任务开始] -- <NSThread: 0x600003bdc6c0>{number = 1, name = main}
    2021-04-02 14:30:09.694932+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 0
    2021-04-02 14:30:09.695138+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
    2021-04-02 14:30:09.695302+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 1
    2021-04-02 14:30:09.695468+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
    2021-04-02 14:30:09.695619+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 2
    2021-04-02 14:30:09.695794+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
    2021-04-02 14:30:09.695957+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 3
    2021-04-02 14:30:09.696411+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
    2021-04-02 14:30:09.696766+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 4
    2021-04-02 14:30:09.697152+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
    2021-04-02 14:30:09.697933+0800 001---函数与队列[79317:12540708] [任务结束] -- <NSThread: 0x600003bdc6c0>{number = 1, name = main}
    
    • 串行队列 + 异步函数:任务是一个接着一个完成,会开辟新的线程
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
        dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL);
        for(int i = 0; i<5; i++){
            dispatch_async(queue, ^{
                NSLog(@"串行队列 + 异步执行 + %d", i);
                NSLog(@"%@", [NSThread currentThread]);
            });
        }
        NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
    }
    
    //执行结果如下
    2021-04-02 14:58:21.928131+0800 001---函数与队列[79640:12562323] [任务开始] -- <NSThread: 0x600001edc400>{number = 1, name = main}
    2021-04-02 14:58:21.928465+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 0
    2021-04-02 14:58:21.928495+0800 001---函数与队列[79640:12562323] [任务结束] -- <NSThread: 0x600001edc400>{number = 1, name = main}
    2021-04-02 14:58:21.928672+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
    2021-04-02 14:58:21.928861+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 1
    2021-04-02 14:58:21.929018+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
    2021-04-02 14:58:21.929183+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 2
    2021-04-02 14:58:21.929364+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
    2021-04-02 14:58:21.929862+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 3
    2021-04-02 14:58:21.930451+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
    2021-04-02 14:58:21.930978+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 4
    2021-04-02 14:58:21.931508+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
    
    • 并发队列 + 同步函数:任务是一个接着一个完成,不会开辟新的线程。
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
        dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
        for(int i = 0; i<5; i++){
            dispatch_sync(queue, ^{
                NSLog(@"并发队列 + 同步执行 + %d", i);
                NSLog(@"%@", [NSThread currentThread]);
            });
        }
        NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
    }
    
    //执行结果如下
    2021-04-02 15:02:05.764874+0800 001---函数与队列[79685:12565854] [任务开始] -- <NSThread: 0x600000c3c5c0>{number = 1, name = main}
    2021-04-02 15:02:05.765200+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 0
    2021-04-02 15:02:05.765423+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
    2021-04-02 15:02:05.765595+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 1
    2021-04-02 15:02:05.765773+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
    2021-04-02 15:02:05.765933+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 2
    2021-04-02 15:02:05.766366+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
    2021-04-02 15:02:05.766516+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 3
    2021-04-02 15:02:05.766727+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
    2021-04-02 15:02:05.767189+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 4
    2021-04-02 15:02:05.767685+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
    2021-04-02 15:02:05.768114+0800 001---函数与队列[79685:12565854] [任务结束] -- <NSThread: 0x600000c3c5c0>{number = 1, name = main}
    
    • 并发队列 + 异步函数:任务执行没有顺序,会开辟新的线程
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
        dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
        for(int i = 0; i<5; i++){
            dispatch_async(queue, ^{
                NSLog(@"并发队列 + 异步执行 + %d", i);
                NSLog(@"%@", [NSThread currentThread]);
            });
        }
        NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
    }
    
    //执行结果如下
    2021-04-02 15:04:09.229740+0800 001---函数与队列[79720:12568189] [任务开始] -- <NSThread: 0x600001254a80>{number = 1, name = main}
    2021-04-02 15:04:09.230070+0800 001---函数与队列[79720:12568287] 并发队列 + 异步执行 + 1
    2021-04-02 15:04:09.230078+0800 001---函数与队列[79720:12568293] 并发队列 + 异步执行 + 0
    2021-04-02 15:04:09.230078+0800 001---函数与队列[79720:12568291] 并发队列 + 异步执行 + 2
    2021-04-02 15:04:09.230091+0800 001---函数与队列[79720:12568189] [任务结束] -- <NSThread: 0x600001254a80>{number = 1, name = main}
    2021-04-02 15:04:09.230113+0800 001---函数与队列[79720:12568286] 并发队列 + 异步执行 + 3
    2021-04-02 15:04:09.230340+0800 001---函数与队列[79720:12568287] <NSThread: 0x600001214880>{number = 5, name = (null)}
    2021-04-02 15:04:09.230347+0800 001---函数与队列[79720:12568293] <NSThread: 0x600001254640>{number = 6, name = (null)}
    2021-04-02 15:04:09.230363+0800 001---函数与队列[79720:12568291] <NSThread: 0x6000012189c0>{number = 4, name = (null)}
    2021-04-02 15:04:09.230494+0800 001---函数与队列[79720:12568286] <NSThread: 0x600001216000>{number = 7, name = (null)}
    2021-04-02 15:04:09.230574+0800 001---函数与队列[79720:12568287] 并发队列 + 异步执行 + 4
    2021-04-02 15:04:09.233691+0800 001---函数与队列[79720:12568287] <NSThread: 0x600001214880>{number = 5, name = (null)}
    
    • 主队列 + 同步函数:形成死锁
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
        dispatch_queue_t queue = dispatch_get_main_queue();
        for(int i = 0; i<5; i++){
            dispatch_sync(queue, ^{
                NSLog(@"主队列 + 同步执行 + %d", i);
                NSLog(@"%@", [NSThread currentThread]);
            });
        }
        NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
    }
    

    总结:主队列+同步函数时,会造成死锁。造成死锁原因如下:

    1. 主队列为串行队列,队列的特性为先进先出,执行完前面的任务才会开始执行后面的任务。
    2. 同步执行的特点是,必须等待当前语句执行完毕,才会执行下一条语句。
    3. viewDidLoad函数是主队列中正在执行的任务

    因此,当新的任务block添加至主队列中时,block任务在队列中位于viewDidLoad函数后面,因此需要viewDidLoad函数执行完成之后才将开始执行block任务。而同步执行的特点要求,必须执行完block的内容后再继续下一条语句。这样就造成了相互等待的情况,即死锁。

    • 主队列 + 异步函数:按照主队列中任务顺序执行,不会开辟新线程。
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
        dispatch_queue_t queue = dispatch_get_main_queue();
        for(int i = 0; i<5; i++){
            dispatch_async(queue, ^{
                NSLog(@"主队列 + 异步执行 + %d", i);
                NSLog(@"%@", [NSThread currentThread]);
            });
        }
        NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
        
    //执行结果如下
    2021-04-02 15:08:38.209535+0800 001---函数与队列[79797:12573195] [任务开始] -- <NSThread: 0x600003610240>{number = 1, name = main}
    2021-04-02 15:08:38.210107+0800 001---函数与队列[79797:12573195] [任务结束] -- <NSThread: 0x600003610240>{number = 1, name = main}
    2021-04-02 15:08:38.261887+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 0
    2021-04-02 15:08:38.263527+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
    2021-04-02 15:08:38.264620+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 1
    2021-04-02 15:08:38.267018+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
    2021-04-02 15:08:38.268366+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 2
    2021-04-02 15:08:38.270540+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
    2021-04-02 15:08:38.271863+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 3
    2021-04-02 15:08:38.274142+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
    2021-04-02 15:08:38.275433+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 4
    2021-04-02 15:08:38.279348+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
    
    • 全局队列 + 同步函数:与并发队列+同步函数的情况相同,任务是一个接着一个完成,不会开辟新的线程。
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        for(int i = 0; i<5; i++){
            dispatch_sync(queue, ^{
                NSLog(@"全局队列 + 同步执行 + %d", i);
                NSLog(@"%@", [NSThread currentThread]);
            });
        }
        NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
    }
    
    //执行结果如下
    2021-04-02 15:14:09.630073+0800 001---函数与队列[79868:12578208] [任务开始] -- <NSThread: 0x6000023e4440>{number = 1, name = main}
    2021-04-02 15:14:09.630321+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 0
    2021-04-02 15:14:09.630523+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
    2021-04-02 15:14:09.630735+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 1
    2021-04-02 15:14:09.630912+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
    2021-04-02 15:14:09.631072+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 2
    2021-04-02 15:14:09.631251+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
    2021-04-02 15:14:09.631396+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 3
    2021-04-02 15:14:09.631567+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
    2021-04-02 15:14:09.631715+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 4
    2021-04-02 15:14:09.632059+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
    2021-04-02 15:14:09.632473+0800 001---函数与队列[79868:12578208] [任务结束] -- <NSThread: 0x6000023e4440>{number = 1, name = main}
    
    • 全局队列 + 异步函数:与并发队列+异步函数的情况相同,任务执行没有顺序,会开辟新的线程。
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        for(int i = 0; i<5; i++){
            dispatch_async(queue, ^{
                NSLog(@"全局队列 + 异步执行 + %d", i);
                NSLog(@"%@", [NSThread currentThread]);
            });
        }
        NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
    }
    
    //执行结果如下
    2021-04-02 15:17:26.686035+0800 001---函数与队列[79911:12581270] [任务开始] -- <NSThread: 0x6000016bc600>{number = 1, name = main}
    2021-04-02 15:17:26.686456+0800 001---函数与队列[79911:12581270] [任务结束] -- <NSThread: 0x6000016bc600>{number = 1, name = main}
    2021-04-02 15:17:26.686429+0800 001---函数与队列[79911:12581363] 全局队列 + 异步执行 + 1
    2021-04-02 15:17:26.686475+0800 001---函数与队列[79911:12581362] 全局队列 + 异步执行 + 2
    2021-04-02 15:17:26.686511+0800 001---函数与队列[79911:12581367] 全局队列 + 异步执行 + 0
    2021-04-02 15:17:26.686623+0800 001---函数与队列[79911:12581360] 全局队列 + 异步执行 + 3
    2021-04-02 15:17:26.686779+0800 001---函数与队列[79911:12581363] <NSThread: 0x600001683040>{number = 6, name = (null)}
    2021-04-02 15:17:26.687020+0800 001---函数与队列[79911:12581362] <NSThread: 0x6000016a5bc0>{number = 4, name = (null)}
    2021-04-02 15:17:26.687293+0800 001---函数与队列[79911:12581366] 全局队列 + 异步执行 + 4
    2021-04-02 15:17:26.687970+0800 001---函数与队列[79911:12581367] <NSThread: 0x6000016f8a00>{number = 7, name = (null)}
    2021-04-02 15:17:26.688492+0800 001---函数与队列[79911:12581360] <NSThread: 0x6000016a5e80>{number = 5, name = (null)}
    2021-04-02 15:17:26.691704+0800 001---函数与队列[79911:12581366] <NSThread: 0x6000016b2840>{number = 3, name = (null)}
    

    总结

    函数\队列 串行队列 并发队列 主队列 全局队列
    同步执行 1. 顺序执行;
    2.不会开辟新线程
    1. 顺序执行;
    2.不会开辟新线程
    死锁 1. 顺序执行;
    2.不会开辟新线程
    异步执行 1. 顺序执行;
    2.会开辟新线程
    1. 乱序执行;
    2.会开辟新线程
    1. 顺序执行;
    2.不会开辟新线程
    1. 乱序执行;
    2.会开辟新线程

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

    下面代码的输出顺序是什么?

    - (void)interview01{
        //并行队列
        dispatch_queue_t queue = dispatch_queue_create("com.HQ.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
    

    分析:

    • 主线程的任务队列为:任务1、异步block1、任务5,其中异步block1会比较耗费性能,任务1和任务5的任务复杂度是相同的,所以任务1和任务5优先于异步block1执行
    • 在异步block1中,任务队列为:任务2、异步block2、任务4,其中block2相对比较耗费性能,任务2和任务4是复杂度一样,所以任务2和任务4优先于block2执行
    • 最后执行block2中的任务3
    • 在极端情况下,可能出现 任务2先于任务1和任务5执行,原因是出现了当前主线程卡顿或者 延迟的情况

    对代码进行修改,将并行队列改成串行队列,其结果是什么?
    答:因为代码里面都是异步执行,所以结果仍然是“1 5 2 4 3”

    继续修改代码,在任务5之前,休眠2s,即sleep(2),结果是什么?
    答:结果可能变成“1 2 4 3 5”

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

    下面代码的输出顺序是什么?

    - (void)interview02{
        //并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.HQ.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、异步block1、任务5,其中异步block1会比较耗费性能,任务1和任务5的任务复杂度是相同的,所以任务1和任务5优先于异步block1执行
    • 在异步block1中,任务队列为:任务2、同步block2、任务4,由于block2为同步执行,因此执行顺序为任务2 -> 同步block2 -> 任务4

    【面试题 - 3】异步函数嵌套同步函数 + 串行队列(即同步队列)

    下面代码的执行顺序是什么?

    - (void)interview03{
        // 同步队列
        dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", NULL);
        NSLog(@"1");
        // 异步函数
        dispatch_async(queue, ^{
            NSLog(@"2");
            // 同步函数
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }
    
    //输出结果:
    1 5 2 死锁
    

    分析:

    • 主线程的任务队列为:任务1、异步block1、任务5,其中异步block1会比较耗费性能,任务1和任务5的任务复杂度是相同的,所以任务1和任务5优先于异步block1执行
    • 在异步block1中,任务队列为:任务2、同步block2、任务4。由于block1是同步队列,同步队列的执行顺序为:任务2->同步block2->任务4。
    • 在同步block2的串行队列中添加了任务3,同步队列的执行顺序为:任务2->同步block2->任务4->任务3。此时就出现了死锁。因为同步block2的执行依赖于任务3完成,而任务3的执行又依赖于任务4的执行完成,而任务4的完成依赖于同步block2的执行完成。

    扩展:如果在上题的基础上去掉任务4,会发生什么?
    答:仍然是发生死锁。因为此时任务3等待的是block2执行完毕,而block2等待任务3执行完成。

    【面试题 - 4 - 新浪】 异步函数 + 同步函数 + 并发队列

    下面代码的执行顺序是什么?

    - (void)interview04{
        //并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.HQ.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 无序)
    

    分析:

    • 任务1 和 任务2由于是异步函数+并发队列,会开启线程,所以没有固定顺序,
    • 任务7、任务8、任务9也是异步函数+并发队列,会开启线程,所以没有固定顺序
    • 任务3是同步函数+并发队列,同步函数会阻塞主线程,但是也只会阻塞0,所以,可以确定的是 0一定在3之后,在789之前
    • 任务1、任务2、任务3复杂度相差不大,因此任务1、任务2、任务3执行无序。

    下面代码中,队列的类型有几种?

    //串行队列 - Serial Dispatch Queue
    dispatch_queue_t serialQueue = dispatch_queue_create("com.HQ.Queue", NULL);
        
    //并发队列 - Concurrent Dispatch Queue
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.HQ.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);
    

    答:队列类型共有两种:串行队列和并发队列,主队列系统提供的串行队列,全局队列是系统提供的并发队列。

    相关文章

      网友评论

          本文标题:OC底层原理 - 20 GCD之应用篇

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