GCD问题:队列 + 任务 是否等于 死锁

一:GCD 线程保活功能

  • GCD 没有线程保活功能。
  • 线程保活功能 只能通过 runloop 来执行。
  • GCD 只是在block 中执行代码,在block 中开启runloop。
  • GCD 和 runloop 不是同一回事。
  • GCD 只负责开启线程,然后去执行任务。不负责线程保活。
  • 就算 GCD 的线程活下来了,也是因为 GCD里面用了 runloop。

使用sync 函数 往当前 串行队列中添加任务,会卡住当前的串行队列(产生死锁)

二:主队列 + 同步任务 = 死锁

- (void)viewDidLoad {
    [super viewDidLoad];
    
     NSLog(@"执行任务1");
    [self GCDDeadlock];
     NSLog(@"执行任务3");
}

- (void)GCDDeadlock {
    // 问题: 以下代码是在主线程执行的,会不会产生死锁?
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"执行任务2");
    })
}

执行结果:
在这里插入图片描述

  • 崩溃信息: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  • 主队列 + 同步任务 = 死锁

第一种解释:

  • dispatch_get_main_queue() : 添加到主队列,代表要添加到主线程执行任务
  • dispatch_sync 的block中的代码是要添加到主队列,意味着: 要添加到主线程中去执行
  • 而dispatch_sync是同步任务, 意味着:要在当前线程去执行。
  • 在当前线程中执行,又是主队列。那就意味着,block 中的内容要放在主线程中执行
  • 可能会认为 这不就直接 1, 2, 3 的执行了么? 为什么会死锁。
  • 因为 queue 是队列,队列的一个基本含义就是:排队,FIFO(先进先出First In First Out). 哪个任务先进来,就先执行哪个任务。
  • 首先,将 block 中的任务 放到 main_queue(主队列)中,将来主队列 需要取出里面的任务来执行。
  • 但实际上主线程已经有ViewDidLoad 这个任务了。 ViewDidLoad 方法的执行就是一个完整的任务
  • 如果想要从 queue 中取出 block 中的任务的话,需要等 上一个(ViewDidLoad)任务执行完毕,才能执行这个任务。
  • 由于 block 任务是 dispatch_sync (马上在当前线程中执行任务),也就意味着,我希望马上要执行完block 里面的东西。执行完 block 中的内容后,才能执行下边的任务(NSLog(@“执行任务3”);这句代码)。

结论:

  • 要想执行 block中的内容,需要 先把 ”执行任务3“ 执行完毕,但是要想执行 ”执行任务3“ 就得把 dispatch_sync 执行完毕。
  • 任务2 等任务3 执行完毕;
  • 任务3 等任务2 执行完毕;
  • 最终导致死锁。

第二种解释:

  • queue 是主队列,会在主线程执行任务
  • dispatch_sync 是同步任务: 在当前线程中执行任务,不具备开启新线程的能力
  • 主队列 + 同步任务:没有开启新线程,串行执行任务
  • 串行执行任务:一个任务执行完毕后,再执行下一个任务。
  • ViewDidLoad 方法 是一个任务
  • dispatch_sync 的 block 也是一个任务
  • 从上到下执行任务,按理说应该是 “任务1”, “任务2” ,“任务3”。 但是 dispatch_sync 是同步扔一个 block 到 queue 中,扔了 我就等着。等到 queue 排队把这个block 执行完了之后,才继续执行下一行代码。
  • 比如 viewDidLoad 在书线程 ,排队为 A;
  • dispatch_sync 也在主线程,排队为B;
  • A 在执行一半的时候,需要等到自己的下一个任务B 执行完,自己才能继续执行。
  • 而 B 排队在后面,需要等A 执行完毕才能执行。
  • 这时死锁就产生了。 A 和 B 互相等待执行,当前线程就卡死在 dispatch_sync 这行代码处。

三、主队列 + 异步任务 = 不死锁

- (void)viewDidLoad {
    [super viewDidLoad];
    
     NSLog(@"执行任务1");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
    });

     NSLog(@"执行任务3");
}

打印结果:
在这里插入图片描述

  • dispatch_async 不要求立马在当前线程同步执行任务

四、 会不会产生死锁

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"执行任务1");

    dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
        
        dispatch_sync(queue, ^{
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}

执行结果:会产生死锁

  • 在 dispatch_sync 这行会产生死锁
    在这里插入图片描述

五、两个不一样的队列,会不会产生死锁

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"执行任务1");

    dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
   // 创建并发队列
    dispatch_queue_t queue1 = dispatch_queue_create("WYqueue1", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
        
        dispatch_sync(queue1, ^{
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}

不会产生死锁。
执行顺序是:
在这里插入图片描述

六、两个不同的串行队列,会不会死锁

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"执行任务1");

    dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue1 = dispatch_queue_create("WYqueue1", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
        
        dispatch_sync(queue1, ^{
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}

不会死锁。
执行顺序
在这里插入图片描述

七、两个任务用一个并发队列

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"执行任务1");

    dispatch_queue_t queue = dispatch_queue_create("WYqueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
        
        dispatch_sync(queue, ^{
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}

不会产生死锁
并发队列:可以执行多个任务。
执行结果
在这里插入图片描述

八、全局队列 、并发队列
在这里插入图片描述

  • 全局队列:不管创建几次,地址都一样;
  • 并发队列:不管创建几次,地址都不一样。

打印地址结果:
在这里插入图片描述

参考:
如何安全使用dispatch_sync

猜你喜欢

转载自blog.csdn.net/M316625387/article/details/83743641