美文网首页程序员
iOS gcd线程死锁问题

iOS gcd线程死锁问题

作者: Good_Citizen | 来源:发表于2020-07-08 11:22 被阅读0次

    同步异步决定是否具备开启线程的能力
    串行并行决定代码执行的先后顺序

    先看下这几个场景,每个场景中的代码执行后会打印什么,为什么?

    场景一:

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        [self test1];
    }
    
    -(void)test1{
    //    主队列 串行队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        NSLog(@"任务1");
    //    同步任务
        dispatch_sync(queue, ^{
            NSLog(@"任务2");
        });
        NSLog(@"任务3");
    }
    
    @end
    

    答案:会打印任务1,然后程序奔溃


    截屏2020-07-06 14.23.24.png

    原因:由于整个viewDidLoad方法是先加入到主队列中的,然后将viewDidLoad方法中的代码从队列中取出来一步步执行,当执行到dispatch_sync时,就会将任务2的代码加入到主队列中,由于主队列是串行队列,所以必须要让先从主队列中取出来的viewDidLoad方法中的所有代码执行完才能取出后面加入到主队列中的任务2代码来执行,但dispatch_sync同步任务的特点是立即要在当前线程也就是主线程中执行任务2中的代码,这样就形成了互相等待的僵局,这就是死锁!

    场景二:

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        [self test1];
    }
    
    -(void)test1{
    //    主队列 串行队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        NSLog(@"任务1");
    //    异步任务
        dispatch_async(queue, ^{
            NSLog(@"任务2");
        });
        NSLog(@"任务3");
    }
    
    @end
    

    答案:结果打印任务1、任务3、任务2,程序正常运行


    截屏2020-07-06 14.25.33.png

    原因:在执行主队列中的viewDidLoad方法时,碰到dispatch_async时也会将任务2中的代码加入到主队列中,但不同的是dispatch_async并不要求立马执行,可以延后执行,这样viewDidLoad方法执行完后再从主队列中取出刚加入的任务2代码,就不会造成互相等待的情况了

    场景三:

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        [self test1];
    }
    
    -(void)test1{
    //    手动创建的串行队列
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
        NSLog(@"任务1");
    //    同步任务
        dispatch_sync(queue, ^{
            NSLog(@"任务2");
        });
        NSLog(@"任务3");
    }
    

    答案:结果打印任务1、任务2、任务3,程序正常运行


    截屏2020-07-06 14.38.29.png

    原因:在执行主队列中的viewDidLoad方法时,碰到dispatch_sync时就会将任务2中的代码加入到手动创建的串行队列中,同步任务会让任务中代码立即执行,由于任务2中的代码是在其他的串行队列中执行,和主线程没有关系,所以就优先执行任务2中的代码,最后执行任务3

    场景四:

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        [self test1];
    }
    
    -(void)test1{
    //    手动创建的串行队列
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
        NSLog(@"任务1");
    //    异步任务
        dispatch_async(queue, ^{
            NSLog(@"任务2");
        });
        NSLog(@"任务3");
    }
    

    答案:结果打印任务1、任务2、任务3(或者任务1、任务3、任务2),程序正常运行


    截屏2020-07-06 14.46.20.png

    原因:在执行主队列中的viewDidLoad方法时,碰到dispatch_async时就会将任务2中的代码加入到手动创建的串行队列中,由于任务2和任务3不在同一个队列中,而且任务2又是异步执行,会创建子线程,所以任务2和任务3是并发执行的,因此打印结果会有两种情况

    场景五:

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        [self test1];
    }
    
    -(void)test1{
    //    手动创建的串行队列
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
        NSLog(@"任务1");
    //    异步任务
        dispatch_async(queue, ^{  // block1
            NSLog(@"任务2");
    //        同步任务
            dispatch_sync(queue, ^{  // block2
                NSLog(@"任务3");
            });
            NSLog(@"任务4");
        });
        NSLog(@"任务5");
    }
    

    答案:结果打印任务1、任务2(或者任务1、任务5、任务2)程序奔溃,产生死锁


    截屏2020-07-06 15.01.55.png

    原因:在执行主队列中的viewDidLoad方法时,碰到dispatch_async时就会将block1中的代码加入到手动创建的串行队列中,由于任务2和任务5不在同一个队列中,而且任务2又是异步执行,会创建子线程,所以任务2和任务5是并发执行的,因此打印结果会有两种情况,当block1在执行任务2的时候,碰到dispatch_sync就会将block2加入到当前串行队列queue中,由于dispatch_sync是同步任务,需要立即执行blcok2中的代码,但queue是串行队列,需要将已经取出来执行的block1的代码执行完才能执行串行队列中的下一个任务也就是block2,这样就互相等待造成死锁!

    结论:

    正在执行的代码是从串行队列中取出来的,而且再次向该串行队列中加入同步任务就会造成死锁!两者缺一不可!

    1.如果向该串行队列中加入的是异步任务
    2.如果将任务加入到其他队列
    3.如果将该串行队列改为并发队列这三种情况都不会产生死锁!

    相关文章

      网友评论

        本文标题:iOS gcd线程死锁问题

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