美文网首页iOS Developer
GCD队列和死锁解析

GCD队列和死锁解析

作者: bluajack | 来源:发表于2016-11-23 17:38 被阅读185次

    GCD核心概念

    • 任务 :执行GCD函数block(代码块)中的代码
    • 队列 :用来存放任务的队列,遵循FIFO原则

    队列(Queue)详解

    • GCD队列有两种:1、串行队列(Serial Queues) ,2、并发队列(Concurrent Queues)

    • 串行队列 : 让任务按照队列次序依次地执行(一个任务执行完毕后,再执行下一个任务),该队列每次只能执行一个任务,只有当队头的任务执行完毕后从队列中移除,才会继续执行下一个任务。
      例子:排队卖票。

    • 并发队列 : 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)。添加到并发队列中的任务,依然会依次按照任务开始加入队列的次序执行,但是,任务执行的过程都同步进行,不需要等待。并发队列保证任务开始执行的次序是确定的,但是你无法知道执行的次序,执行时长或在任意时间点同步执行的任务个数。

    队列使用

    • 系统队列:系统(“系统”指所有被苹果黑盒封装,未公开源码,我们不能得知的操作,下同)为每个应用提供了一个串行队列与四个并发队列。
    • 主队列:为串行队列,在应用的主线程中执行任务,该队列用于更新应用的 UI,执行与 UIViews 更新相关的所有任务。因此每次只能执行一个任务,所以当你在主队列运行繁重的任务时,UI 就会停止响应。
    dispatch_get_main_queue()
    
    • 四个并发队列:我们称之为全局队列,拥有四个不同的优先级。
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)//第二个参数无用,苹果用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 //优先级最低
    
    • 自己创建队列
    • 可以使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_CONCURRENT表示并发队列。DISPATCH_QUEUE_SERIAL表示串行队列。
    // 串行队列的创建方法
    dispatch_queue_t queue= dispatch_queue_create("bluajack", DISPATCH_QUEUE_SERIAL);
    // 并发队列的创建方法
    dispatch_queue_t queue= dispatch_queue_create("bluajack", DISPATCH_QUEUE_CONCURRENT);
    

    GCD函数创建任务

    • 创建方法

    // 同步执行任务创建方法
    dispatch_sync(queue, ^{ NSLog(@"%@",[NSThread currentThread]); // 这里放任务代码});
    // 异步执行任务创建方法
    dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); // 这里放任务代码});

    - 函数和队列组合方式
     - ```objc
       同步执行 + 串行队列     (不会开启子线程,串行执行任务)
       同步执行 + 并发队列     (不会开启子线程,串行执行任务)
       异步执行 + 串行队列     (开启子线程,串行执行任务)
       异步执行 + 并发队列     (开启子线程,并行执行任务)
       
        //特殊队列- 主队列
        同步执行 + 主队列       (不会开启子线程,串行执行任务)
        异步执行 + 主队列       (不会开启子线程,串行执行任务)
        ```
    - 归纳
     - 1.同步GCD函数执行任务,无论添加的是串行队列还是并发队列,都不会创建子线程,但都会在创建任务后,立刻在当前线程依次按队列先进先出(FIFO)顺序执行任务。
     - 2.主队列是一个特殊的队列,无论是GCD同步函数还是异步函数执行任务,都不会创建子线程。
    
    
    ##分析主队列 + 异步
    - 先贴代码和控制台信息
    - ```objc
     - (void)viewDidLoad {
        [super viewDidLoad];
        
         NSLog(@"1");
        
        //同步GCD函数,无论是串行队列还是并发队友,都不会创建子线程,但都会在创建后,立刻在当前线程依次按队列FIFO顺序执行任务。
        //主队列是一个特殊的队列,无论是GCD同步函数还是异步函数,都不会创建子线程
        //所以只有在主线程执行完主队列里面的内容,空闲时,才能去执行你创建的队列。
        dispatch_async(dispatch_get_main_queue(), ^{
            for (int i = 0; i<5; i++) {
                NSLog(@"222222222%@",[NSThread currentThread]);
            }
        });����
        
        for (int i = 0; i<5; i++) {
            NSLog(@"3");
        }
    }
     - (void)viewWillAppear:(BOOL)animated{
        [super viewWillAppear:animated];
        
        for (int i = 0; i<5; i++) {
            NSLog(@"4");
        }
    }
     - (void)viewDidAppear:(BOOL)animated{
        TestViewController *VC = [[TestViewController alloc] init];
        
        //一直到modal方法结束后,才能在主线程去执行你创建的队列
        //我猜测modal方法内部,是执行了VC控制器的loaView懒加载方法,初始化view,一并执行了viewDidLoad方法
        //交给系统GPU去渲染,然后此时CPU得到空闲,就会去执行我在本控制器内部用GCD在主队列中创建的任务,
        //然后我创建的任务执行完毕,就会继续执行主队列的内容,GPU渲染完毕,通知CPU进行视图的显示,
        //此时就会调用VC的viewWillAppear的代理方法。
        [self presentViewController:VC animated:YES completion:nil];
    }
    

    **2016-11-23 17:06:23.207 test-GCD死锁[14103:166127] 1
    **2016-11-23 17:06:23.207 test-GCD死锁[14103:166127] 3
    **2016-11-23 17:06:23.207 test-GCD死锁[14103:166127] 3
    **2016-11-23 17:06:23.208 test-GCD死锁[14103:166127] 3
    **2016-11-23 17:06:23.208 test-GCD死锁[14103:166127] 3
    **2016-11-23 17:06:23.208 test-GCD死锁[14103:166127] 3
    **2016-11-23 17:06:23.208 test-GCD死锁[14103:166127] 4
    **2016-11-23 17:06:23.208 test-GCD死锁[14103:166127] 4
    **2016-11-23 17:06:23.208 test-GCD死锁[14103:166127] 4
    **2016-11-23 17:06:23.209 test-GCD死锁[14103:166127] 4
    **2016-11-23 17:06:23.209 test-GCD死锁[14103:166127] 4
    **2016-11-23 17:06:23.213 test-GCD死锁[14103:166127] -[TestViewController viewDidLoad]
    **2016-11-23 17:06:23.577 test-GCD死锁[14103:166127] 222222222<NSThread: 0x7f8f84507dd0>{number = 1, name = main}
    **2016-11-23 17:06:23.578 test-GCD死锁[14103:166127] 222222222<NSThread: 0x7f8f84507dd0>{number = 1, name = main}
    **2016-11-23 17:06:23.578 test-GCD死锁[14103:166127] 222222222<NSThread: 0x7f8f84507dd0>{number = 1, name = main}
    **2016-11-23 17:06:23.578 test-GCD死锁[14103:166127] 222222222<NSThread: 0x7f8f84507dd0>{number = 1, name = main}
    **2016-11-23 17:06:23.578 test-GCD死锁[14103:166127] 222222222<NSThread: 0x7f8f84507dd0>{number = 1, name = main}
    **2016-11-23 17:06:23.580 test-GCD死锁[14103:166127] -[TestViewController viewWillAppear:]

    - 把上述代码创建的GCD函数内部的代码块的for循坏,加到10000次,你就能很明显的感觉到页面跳转的延迟,这就造成主线程的阻塞。
    
    
    ##GCD死锁
    - 典型例子:在主线程中用GCD创建同步 + 主队列函数
    - ```objc
        - (void)viewDidLoad {
          [super viewDidLoad];
           NSLog(@"1");
           dispatch_async(dispatch_get_main_queue(), ^{
              NSLog(@"2%@",[NSThread currentThread]);
           });���� 
           NSLog(@"3"); 
    }
    

    输出结果为:1 。这个程序就是典型的死锁,只打印了“1”一行,就再也没有响应了,已经造成了GCD死锁。


    下班了,具体分析,你们可以看下面这篇文章,比较详细。
    http://www.jianshu.com/p/bbabef8aa1fe

    相关文章

      网友评论

        本文标题:GCD队列和死锁解析

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