GCD避免死锁的三要素

作者: PotterSun | 来源:发表于2015-10-28 18:30 被阅读1391次

    平时总在用GCD,但你知不知道,GCD一不小心就会出现死锁,如果死锁在主线程上,整个程序就完了,所以避免死锁是我们则无旁贷的责任。

    那我们先看看造成GCD死锁的三要素(以下内容是我个人总结,并写了一些测试用例,如有不对的地方,请大家指正 PS:我把GCD死锁分成 “普通死锁” ,“高级死锁”,"混合死锁")

    普通死锁 必要条件

    1.队列 串行队列:1 并行队列:0
    2.调度方法 同步调用(dispatch_sync):1 异步调用(dispatch_async):0
    3.同一个Q 当前所分发到的Q 和 "外部Q" 是否是同一个Q 同一个:1 不同:0

    PS:"外部Q"是指
    1.当前嵌套在外部的Q 如以下形式,Q1就是外部Q

    dispatch_sync(Q1, ^{
         NSLog(@"b");
         dispatch_sync(Q2, ^{
                NSLog(@"c");
         });
      });)              
    

    2.外部的方法执行时 所在的Q 如以下形式 dead1第二次运行时所在的Q是Q1 它就是外部Q

    - (void)dead1{
        NSLog(@"不死1");
         __weak typeof (self) weakSelf = self;
         dispatch_sync(Q1, ^{
               NSLog(@"第二次运行死");
               [weakSelf dead1];
    });}
    

    #######结论: 如果三个条件同时满足 则死锁,如果其中一个不满足 不会死锁

    高级死锁 必要条件

    1.调度方法 同步阻塞调用(dispatch_barrier_sync):1 异步阻塞调用(dispatch_barrier_async):0
    2.同一个Q 当前所分发到的Q 和 "外部Q" 是否是同一个Q 同一个:1 不同:0

    #######结论: 如果两个条件同时满足 则死锁,如果其中一个不满足 不会死锁 (高级死锁主要针对dispatch_barrier而言)

    混合死锁 必要条件

    1.调度方法 同步阻塞调用(dispatch_barrier_sync)或同步调用(dispatch_sync):1 异步阻塞调用(dispatch_barrier_async)或异步调用(dispatch_async):0
    2.同一个Q 当前所分发到的Q 和 "外部Q" 是否是同一个Q 同一个:1 不同:0

    #######结论: 如果两个条件同时满足 则死锁,如果其中一个不满足 不会死锁 (混合死锁主要针对dispatch_barrier 和 dispatch_async,dispatch_sync嵌套调用而言)


    以下是死锁测试用例

    - (void)viewDidLoad 
    {
         [super viewDidLoad];
        // 创建两个串行队列和一个并行队列
        _serialQueue = dispatch_queue_create("aabbcc", DISPATCH_QUEUE_SERIAL);
        _secondSerialQueue = dispatch_queue_create("ssss", DISPATCH_QUEUE_SERIAL);
        _concurrentQueue = dispatch_queue_create("dddd", DISPATCH_QUEUE_CONCURRENT); 
        [self dead1];
        [self dead2];
        [self dead3];
     }
    

    一 普通死锁

    - (void)dead1
    {
        NSLog(@"不死1");
        __weak typeof (self) weakSelf = self;
        dispatch_sync(_serialQueue, ^{
            NSLog(@"第二次运行死");
            [weakSelf dead1];
        });
    } 
    

    PS:打印日志如下:
    2015-10-29 16:46:35.611 DeadLockTest[19971:204243] 不死1
    2015-10-29 16:46:35.612 DeadLockTest[19971:204243] 第二次运行死
    2015-10-29 16:46:35.612 DeadLockTest[19971:204243] 不死1
    原因 在[weakSelf dead1]; 循环调用方法本身时,它就在_serialQueue队列上运行,此时“dispatch_sync(_serialQueue, ^{}” 满足条件三,同一个Q _serialQueue上 同时又满足 串行Q 和 同步分发 所以在第二次运行时产生死锁。

    - (void)dead2
    {
        NSLog(@"不死1");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"死锁");
        });
    }    
    

    PS:打印日志如下:
    2015-10-29 16:52:45.435 DeadLockTest[20036:206574] 不死1
    原因 dispatch_get_main_queue() 是主Q dead2本身就在主Q 上运行 符合同一个Q 并且同时符合2,3条件 所以死锁

    二 高级死锁

    //死在子线程
    - (void)dead3
    {
        //并行队列死锁
        NSLog(@"a");
        dispatch_async(_concurrentQueue, ^{
            NSLog(@"b");
            dispatch_barrier_sync(_concurrentQueue, ^{
                 NSLog(@"c");
            });
        });
    }
    

    PS:打印日志如下:
    2015-10-29 18:15:02.522 DeadLockTest[27790:256689] a
    2015-10-29 18:15:05.919 DeadLockTest[27790:256851] b
    原因 同时满足 1都是同一个Q _concurrentQueue 2同步分发 使用dispatch_barrier_sync 所以死锁

    三 混合死锁

    //混合模式,内部使用dispatch_sync 又是同一Q _concurrentQueue 死锁
    - (void)dead4
    {
        NSLog(@"不死1");
        dispatch_barrier_async(_concurrentQueue, ^{
            NSLog(@"不死2");
           dispatch_sync(_concurrentQueue, ^{
               NSLog(@"死在子线程上");
           });
        });
    }
    

    PS:打印日志如下:
    2015-10-30 10:13:08.544 DeadLockTest[5346:35961] 不死1
    2015-10-30 10:13:08.550 DeadLockTest[5346:36036] 不死2
    原因 同时满足 1都是同一个Q _concurrentQueue 2同步分发 使用dispatch_sync 所以死锁

    //混合模式,内部使用dispatch_barrier_sync 又是同一Q _concurrentQueue 死锁
    - (void)dead5
    {
        NSLog(@"不死1");
        dispatch_async(_concurrentQueue, ^{
            NSLog(@"不死2");
            dispatch_barrier_sync(_concurrentQueue, ^{
               NSLog(@"死在子线程上");
           });
        });
    }
    

    PS:打印日志如下:
    2015-10-30 10:14:08.544 DeadLockTest[5346:35961] 不死1
    2015-10-30 10:14:08.550 DeadLockTest[5346:36036] 不死2
    原因 同时满足 1都是同一个Q _concurrentQueue 2同步分发 使用dispatch_barrier_sync 所以死锁


    以下是非死锁测试用例

    /*打破条件1  _concurrentQueue 是并行队列 不死锁 */
    - (void)noDead1
    {
        NSLog(@"a");
        dispatch_async(_concurrentQueue, ^{
            NSLog(@"b");
           dispatch_sync(_concurrentQueue, ^{
               NSLog(@"c");
           });
        });
    }
    

    PS:打印日志如下
    2015-10-29 18:22:36.724 DeadLockTest[27922:261502] a
    2015-10-29 18:22:36.725 DeadLockTest[27922:261633] b
    2015-10-29 18:22:36.726 DeadLockTest[27922:261633] c

      /*虽然说三条日志都在主线程上执行,但是因为是在两个不同的队列上 打破条件3  
      不会死锁 _serialQueue和_secondSerialQueue 不是同一个队列*/
    - (void)noDead2
    {
        NSLog(@"不死1");
        dispatch_sync(_serialQueue, ^{
            NSLog(@"不死2");
            dispatch_sync(_secondSerialQueue, ^{
                NSLog(@"不死3");
            });
        });
    }
    

    PS:打印日志如下
    2015-10-29 18:26:23.876 DeadLockTest[27977:263046] 不死1
    2015-10-29 18:26:23.877 DeadLockTest[27977:263046] 不死2
    2015-10-29 18:26:23.878 DeadLockTest[27977:263046] 不死3


    以上就是对死锁的分析,如果掌握了这几个必要条件,赶快用下面的链接练练手吧,检验一下对不对
    http://www.cnblogs.com/tangbinblog/p/4133481.html

    相关文章

      网友评论

        本文标题:GCD避免死锁的三要素

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