iOS GCD死锁的情况和解决办法

1、同步串行主队列

dispatch_sync(dispatch_get_main_queue(), ^(void){
        NSLog(@"这里死锁了");
});

因为dispatch_sync是一个同步队列,所以会堵塞在这里,直至这个dispatch_sync有返回才会继续执行下去,此时这个block是加入到主队列里面,并且后面的代码在block之前加入到主队列,然而主队列是一个FIFO的串行队列,所以block需要后面的代码先执行,但是由于这个dispatch_sync阻塞了,所以后面的代码又没法执行,所以导致相互阻塞了。

解决方案

  • 切换成异步队列
dispatch_async(dispatch_get_main_queue(), ^(void){
        NSLog(@"这里死锁了");    // 现在没有死锁
});
  • 换一个队列,不添加到主队列
dispatch_sync(dispatch_get_global_queue(0, 0), ^(void){
        NSLog(@"这里死锁了");   // 现在没有死锁
});

2、异步执行同步串行队列(任意队列)

dispatch_queue_t queue = dispatch_queue_create("serialQueue",DISPATCH_QUEUE_SERIAL);

NSLog(@"1");//任务1

dispatch_async(queue,^{

    NSLog(@"2");//任务2

    dispatch_sync(queue,^{

        NSLog(@"3");//任务3
    });

    NSLog(@"4");//任务4
});
NSLog(@"5");//任务5

// 1
// 5
// 2

首先任务1正常执行;
因为dispatch_async是一个异步线程,不会堵塞线程,所以任务5也正常执行;
进入dispatch_async里面,任务2可以直接执行;
然后是一个同步线程,并且queue是一个串行队列,遵循FIFO,此时同步线程会阻塞线程,任务3加入到queue队列中,但是任务4又是优先于3加入queue,所以任务3需要等待任务4执行完才执行,但是任务3又堵塞了线程,所以任务4也不能执行,这样相互堵塞造成死锁。

解决方案

  • 将异步线程和同步线程放在不同的队列
dispatch_queue_t queue = dispatch_queue_create("serialQueue",DISPATCH_QUEUE_SERIAL);

NSLog(@"1");//任务1

dispatch_async(queue,^{

    NSLog(@"2");//任务2

    dispatch_sync(dispatch_get_main_queue(),^{

        NSLog(@"3");//任务3
    });

    NSLog(@"4");//任务4
});
NSLog(@"5");//任务5

// 1
// 5
// 2
// 3
// 4
  • 将串行队列换成并发队列
dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_CONCURRENT);

NSLog(@"1");//任务1

dispatch_async(queue,^{

    NSLog(@"2");//任务2

    dispatch_sync(queue,^{

        NSLog(@"3");//任务3
    });

    NSLog(@"4");//任务4
});
NSLog(@"5");//任务5

// 1
// 5
// 2
// 3
// 4

3、堵塞主队列,同步主队列任务死锁

dispatch_async(dispatch_get_global_queue(0,0),^{
        
    NSLog(@"1");//任务1
        
    dispatch_sync(dispatch_get_main_queue(),^{
            
        NSLog(@"2");//任务2
            
    });
    NSLog(@"3");//任务3 
});
    
NSLog(@"4");//任务4
    
while(1){
        
}
NSLog(@"5");//任务5

// 4
// 1

首先是一个异步线程里的全局队列,不会堵塞,所以任务4可以执行;
异步线程里面任务1也可以执行,任务1、4顺序不固定;
然后主线程里的while循环堵塞主队列,但是异步线程的任务2也是加入到主队列里的,并且是在while循环任务之后,while堵塞,任务5无法执行,所以任务2也没法执行,任务2不能返回,那么任务3也不能执行。

  • 用一个并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

dispatch_async(queue,^{
        
    NSLog(@"1");//任务1
        
    dispatch_sync(queue,^{
            
        NSLog(@"2");//任务2
            
    });
   NSLog(@"3");//任务3a
        
});
    
NSLog(@"4");//任务4
    
while(1){
        
}
NSLog(@"5");//任务5

// 4
// 1
// 2
// 3

测试发现如果把queue换成一个自定义的串行队列,结果也只会打印4和1,任务2会死锁,所以可知全局队列是一个并发队列。

总结

造成死锁的原因是:在同一个串行的队列中,同步添加block。

你可能感兴趣的:(iOS GCD死锁的情况和解决办法)