美文网首页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 细细的读

    目录 前言 为什么选择GCD? 串行队列、并行队列、同步、异步 线程死锁解析 DispatchQueue的使用 D...

  • GCD队列和死锁解析

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

  • GCD死锁

    GCD死锁原因 GCD死锁的原因是队列阻塞,而不是线程阻塞! 串行和并行 串行和并行都是相对于队列而言的-队列(负...

  • 2021--- GCD

    gcd同步,异步,串行队列,并发队列,全局队列,主队列,以及死锁。 1、gcd队列阻塞问题[https://www...

  • 同步串行导致的死锁问题

    GCD中同步操作dispatch_sync()中队列使用主队列会导致线程死锁,是众所周知的事情。但是是如何造成死锁...

  • iOS多线程梳理-GCD(3)

    上一篇 iOS多线程梳理-GCD死锁 GCD栅栏函数 仅在自己创建的并发队列上有效,在全局(Global)并发队列...

  • 16-多线程-01

    一、GCD 串行队列产生的死锁:只要使用sync函数往同一个串行队列中添加任务,就会产生死锁。只要改为async函...

  • 2022-11-17 08多线程

    GCD 同步串行 死锁!,会导致队列引起循环等待 没有问题 同步并发 答案:12345 产生死锁 异步串行 异步并...

  • Swift- 多线程编程GCD

    参考文章:1、Swift 3使用GCD和DispatchQueues2、线程死锁 基本概念: 串行队列:只有一个线...

  • iOS死锁原理和解决方案

    原文链接 - 彻底搞懂OC中GCD导致死锁的原因和解决方案 GCD提供了功能强大的任务和队列控制功能,相比于NSO...

网友评论

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

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