美文网首页
GCD让我死锁的死锁(Two)

GCD让我死锁的死锁(Two)

作者: 拧发条鸟xds | 来源:发表于2018-08-18 10:49 被阅读0次

    本文目录:

    • 同步执行主队列(在主线程中)
    • 另几种死锁情况

    本文参考文章链接:


    GCD2之死锁

    学习GCD,然后搞的我有点死锁了。下面是研究的一点死锁案例和原理...嗯...感觉还是总结的不太到位,可以看看第一个文章链接

    同步执行主队列(在主线程中)

    在主线程中,同步执行主队列,会造成死锁。

    分析:在主线程,当前队列为主队列,然后使用同步执行提交任务到主队列,死锁。下面有详细的原理解释。

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        //主队列
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
        NSLog(@"开始"); 
        
        //异步提交一个任务到串行队列
        dispatch_sync(mainQueue, ^{
    
            NSLog(@"同步执行");
    
        });
        
        NSLog(@"结束");
    }
    

    输出:

    2018-08-17 11:21:04.826284+0800 GCD[882:81994] 开始
    
    然后就崩溃了
    

    原理分析

    • 同步执行:必须执行完当前任务才能继续往下走,并且不具备开启多线程的能力,只能交给当前线程来执行(如果队列是主队列,会交给主线程来执行)。
    • 主队列,只能交给主线程来执行。平常的程序,不使用多线程的情况下,一般都是放在主队列中的。
    1. 现在,我们将viewDidLoad{}看作是一个任务,它是在主队列中的,交给主线程来执行的,它排在主队列的队首。

    2. 现在主线程正在执行将这个任务viewDidLoad{},主线程在这个任务里遇到了一个同步执行,同步执行必须将任务完成才能继续往下走( 这里的任务指NSLog(@"同步执行"); ),然后同步执行将这个任务提交给了主队列,想让主线程先完成这个任务。

    3. 但是,注意了,但是,主队列里面有一个任务viewDidLoad{}了,同步执行将这个任务( NSLog(@"同步执行"); )提交给主队列后,只能排在任务viewDidLoad{}后面。主线程必须执行完任务viewDidLoad{},才能执行任务( NSLog(@"同步执行"); ),但是同步执行要求必须先执行完任务( NSLog(@"同步执行"); ),才能继续往下走。主线程说,我得按顺序执行啊,执行完前面的任务,才能执行后面的;同步执行说,不行,就得先执行我的,才能继续下去。好吧,结果就这样卡住了,谁也执行不了。

    这就是死锁。

    怎么在当前情况下怎么解开这个锁呢?

    1. 改用异步执行。因为异步执行会立即返回,不管任务到底是怎么被执行的。过程:主线程先执行主队列里的任务viewDidLoad{},在这个任务里遇到了异步执行,异步执行立即返回,任务继续,输出NSLog(@"结束"); ,然后任务viewDidLoad{}执行完成;同时,在异步执行返回的同时,异步执行将任务( NSLog(@"同步执行"); )提交给了主队列,主队列在执行完viewDidLoad{}后,就执行任务( NSLog(@"同步执行"); ),输出NSLog(@"同步执行")。结束。

    2. 改用其它队列:使用 dispatch_queue_create 创建一个队列。同步执行将任务提交给一个其它的队列,这里同步执行当前线程,即主线程执行任务。主线程在执行任务viewDidLoad{}时,碰到了同步执行,同步执行必须执行完( NSLog(@"同步执行");)才能继续,同时这个任务提交给了其它的队列,主线程切换队列,先执行其它队列里的这个任务( NSLog(@"同步执行");),在切回到主队列继续执行任务viewDidLoad{}。

    3. 上两个方法混用,就不赘述了。

    另几种死锁情况

    1. 嵌套两个同步执行,且同步执行的是同一个串行队列,会出现死锁

    同步执行嵌套,同一个串行队列:

        dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(serialQueue, ^{
            
            NSLog(@"1 %@", [NSThread currentThread]);
    
            dispatch_sync(serialQueue, ^{
                NSLog(@"2 %@", [NSThread currentThread]);
            });
    
            NSLog(@"3 %@", [NSThread currentThread]);
            
        });
    
    1. 但如果两个串行队列不是同一个,就不会出现死锁。

    同步执行嵌套,不为同一个串行队列,不会死锁:

        dispatch_queue_t serialQueue1 = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
        
        dispatch_queue_t serialQueue2 = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(serialQueue1, ^{
            
            NSLog(@"1 %@", [NSThread currentThread]);
            
            dispatch_sync(serialQueue2, ^{
                NSLog(@"2 %@", [NSThread currentThread]);
            });
    
            NSLog(@"3 %@", [NSThread currentThread]);
            
        });
        
        NSLog(@"4 %@", [NSThread currentThread]);
    
    1. 上面这种情况不会出现死锁,而且输出按照顺序。这里我出现了疑惑,不是同步执行必须等到block的结果返回,才能继续吗?可以在同步执行里面,使用同步执行去执行吗?我再试验了一下,发现,里面的串行队列换成并发队列,也不会出现死锁。

    同步执行嵌套,外面为串行队列,里面为并发队列,不会死锁:

        dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
        
        dispatch_queue_t concurrentQueue = dispatch_queue_create("first", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_sync(serialQueue, ^{
            
            NSLog(@"1 %@", [NSThread currentThread]);
            
            dispatch_sync(concurrentQueue, ^{
                NSLog(@"2 %@", [NSThread currentThread]);
            });
    
            NSLog(@"3 %@", [NSThread currentThread]);
            
        });
        
        NSLog(@"4 %@", [NSThread currentThread]);
    
    1. 然后我又在别人的博客上发现了一种死锁...我越来越怀疑自己了...

    外面是异步串行队列,里面是同步串行队列,串行队列为同一个,死锁:

    dispatch_queue_t serialQueue = dispatch_queue_create("fff", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    dispatch_async(serialQueue, ^{
        NSLog(@"2");
        dispatch_sync(serialQueue, ^{  
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
    

    这种情况,也会发生死锁,死锁在里面的同步执行。分析:同一个串行队列,外面是异步,可以开启另一条线程,但是串行队列一次只能执行一个任务,所以里面的同步执行死锁了。


    ****然后我总结了一下,下面是我自己的理解,可能不对,烦请指正****。

    1. 在同步执行的任务里,是可以再异步或同步执行的

    2. 并发队列不会造成死锁

    注意,并发队列不会造成死锁。****因为并发队列可以一次执行多个任务****。

    1. 只有串行队列会造成死锁,因为串行队列一次只能执行队列的一个任务,当你在执行这个任务时,又提交了一个任务到这个串行队列,并要求同步执行时,会出现死锁。

    上面的第一个例子就是这样, 主队列(是串行队列)正在被主线程执行任务,这时要求同步执行,并提交了一个任务到主队列,出现死锁。

    中间那个死锁的例子和上面的原理相同。

    最后的一个死锁的例子。虽然外面是异步执行,但是串行队列要求执行完这个,才能执行下一个。异步执行的那个任务还没完,就要求同步执行下一个任务;串行队列的第一个任务还没出去呢,就要求执行第二个,这肯定不行。


    再看另一个死锁的例子:

        dispatch_queue_t concurrentQueue = dispatch_queue_create("first", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(serialQueue, ^{
            
            NSLog(@"1 %@", [NSThread currentThread]);
            
            dispatch_sync(concurrentQueue, ^{
                
                NSLog(@"2 %@", [NSThread currentThread]);
                
                dispatch_sync(serialQueue, ^{
                    
                    NSLog(@"3 %@", [NSThread currentThread]);
                    
                });
                
            });
    
        });
    

    中间的换成异步执行就不会死锁:

        dispatch_sync(serialQueue, ^{
            
            NSLog(@"1 %@", [NSThread currentThread]);
            
            dispatch_async(concurrentQueue, ^{
                
                NSLog(@"2 %@", [NSThread currentThread]);
                
                dispatch_sync(serialQueue, ^{
                    
                    NSLog(@"3 %@", [NSThread currentThread]);
                    
                });
                
            });
    
        });
    

    我也是闲的...搞的我有点糊涂了,最后还是说,死锁得具体问题具体分析。

    ****总结一点,死锁归根到底,就是串行队列被阻塞了****。

    大家可以看看这篇文章:理解GCD死锁

    相关文章

      网友评论

          本文标题:GCD让我死锁的死锁(Two)

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