美文网首页
关于向主线程添加同步任务造成死锁的思考

关于向主线程添加同步任务造成死锁的思考

作者: innepeace | 来源:发表于2017-05-26 10:13 被阅读0次

有这样一个例子,即在主线程开启同步任务死锁的例子:

NSLog(@"1"); // 任务1
dispatch_sync(dispatch_get_main_queue(), ^{
   NSLog(@"2"); // 任务2
});
NSLog(@"3"); // 任务3

关于这个例子如何会死锁,网上也有很详细的解释。不过可能对于某些基础不是很扎实的同学来说,有些地方不太容易理解。这里,我说一下自己的理解,希望对你有所帮助。

如大家所说,造成这种死锁的原因在于:

1.dispatch_sync,同步执行;
2.dispatch_get_main_queue(),主队列。这里先说一下为什么会造成死锁,后面再介绍其他内容;

第一点,先让我们来看看dispatch_sync和dispatch_async,按照字面意思理解前者是同步的,后者是异步的。苹果给出的文档中,dispatch_sync的解释是:Submits a block for synchronous execution on a dispatch queue。翻译之后就是,向队列中,提交一个同步执行的block。同时,文档中也有这样一句话:dispatch_sync() will not return until the block has finished。就是说,只有当block中的内容执行完之后,才会返回之前插入的地方继续执行。

相对应的,dispatch_async的解释是:Submits a block for asynchronous execution on a dispatch queue。翻译之后就是,向队列中,提交一个异步执行的block。同样的,此时不会等待block的执行,会直接执行之后的代码,而将block交给其他线程。这里暂且不说,后面再聊。

第二点,dispatch_get_main_queue(),苹果给出的解释是:Returns the default queue that is bound to the main thread。也就是说,它返回了依靠主线程来执行任务的队列。这里涉及到Runloop,简单理解就是,iOS程序有一个一直在执行的线程,这个线程会一直运行直到被叫停。这个线程和主队列是绑定的,就是用来执行主队列的任务。

那么现在把它们放在一起考虑,系统一直在顺序执行主队列的任务(通过主线程),此时阻塞主线程(dispatch_sync)向主队列队尾添加一个任务(不知道主队列此时有没有任务在执行,不care)。dispatch_sync必须等到block执行完才会返回当前线程(主线程)继续往下执行,当然下一个任务仍然来自于主队列。那么,此时主线程在等待主队列给出下一个任务(因为主线程与主队列是绑定的,只能根据FIFO原则顺序执行主队列的任务);可是主队列也在等待,它在等待主线程将block执行完成才会给主线程另外一个任务。主线程和主队列在互相等待,那么就造成了死锁。

这个原理确实很绕口,在看了很多博客之后,总算有点儿眉目。本来我是无法理解为什么会造成死锁的,直到想通了上面关节,就是主线程和主队列互相等待。在想通这些的过程中,我做了另外的工作来证实这种想法,或者说另外的这些让我想通了这个关节。让我们来看另外两个例子。


这里有一个不会死锁的例子:

    NSLog(@"1,NSThread:%@",[NSThread currentThread]); // 任务1
    dispatch_async(queueC, ^{
        NSLog(@"2,NSThread:%@",[NSThread currentThread]); // 任务2
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"3,NSThread:%@",[NSThread currentThread]); // 任务3
        });
        NSLog(@"4,NSThread:%@",[NSThread currentThread]); // 任务4
    });
    NSLog(@"5,NSThread:%@",[NSThread currentThread]); // 任务5

输出结果是:

image.png

在这个例子中,用到了 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"3,NSThread:%@",[NSThread currentThread]); // 任务3 });然而并没有出现线程死锁现象,控制台正常打印了1 5 2 3 4 。那么为什么这里不会造成死锁呢?原因就在于dispatch_sync是阻塞了当前线程来给后面队列添加任务。也就是说,在这里,dispatch_sync阻塞了number = 3 的线程,将block添加入主队列,之后由主线程(与主队列绑定)执行打印任务。接着完成之后,再由*number = 3 *的线程执行任务4,那么当然不会造成线程死锁。


下面有一个会死锁的例子:

    dispatch_queue_t queueS = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL); // 串行队列
     NSLog(@"1,NSThread:%@",[NSThread currentThread]); // 任务1
    dispatch_async(queueS, ^{
        NSLog(@"2,NSThread:%@",[NSThread currentThread]); // 任务2
        dispatch_sync(queueS, ^{
            NSLog(@"3,NSThread:%@",[NSThread currentThread]); // 任务3
        });
        NSLog(@"4,NSThread:%@",[NSThread currentThread]); // 任务4
    });
    NSLog(@"5,NSThread:%@",[NSThread currentThread]); // 任务5

它的输出是:

image.png
我们看到,它在执行到 dispatch_sync(queueS, ^{ NSLog(@"3,NSThread:%@",[NSThread currentThread]); // 任务3 });的时候,出现了死锁,回头看一下它的打印结果,任务1和任务5都是主线程执行的,而任务2是number = 3 的线程执行的,也就是说,dispatch_sync阻塞了number = 3 的线程,同时,这个线程执行队列queueS里面的任务**。这就等价于在主线程向主队列同步插入block造成死锁,因为线程和队列相互等待。

弄清楚线程和队列的关系,这个问题就变得很简单了不是吗?

相关文章

  • 关于向主线程添加同步任务造成死锁的思考

    有这样一个例子,即在主线程开启同步任务死锁的例子: 关于这个例子如何会死锁,网上也有很详细的解释。不过可能对于某些...

  • GCD分析(中)

    同步函数死锁 死锁现象 主线程因为同步函数的原因等着先执⾏任务 主队列等着主线程的任务执⾏完毕再执⾏⾃⼰的任务 主...

  • iOS多线程总结

    多线程 优缺点,实际应用多线程比较死锁:使用同步sync,向同一个/当前的串行队添加任务,会产生死锁新等旧,旧等新...

  • GCD死锁情况

    造成死锁的情况分析 第一种是:主队列同步任务很容易造成死锁,因为主队列是在主线程空闲时才会调度队列中的任务在主线程...

  • 死锁gcd

    串行队列中添加同步会造成死锁,互相等待同步任务执行完才能执行下一个 全局的并行队列不会死锁

  • GCD线程死锁

    GCD 以下情况下会死锁,(不考虑线程锁的情况下) 主队列中同步添加任务。 串行队列任务中添加同步任务 为什么呢?...

  • 关于主线程执行同步任务造成死锁的思考

    经典问题重现 bug 粘贴主线程+同步造成死锁 要想明白,为什么造成死锁,首先要搞懂主线程是干嘛的 主线程程序运行...

  • iOS笔记-多线程

    同步函数&串行队列 不会开启线程,在当前线程执行任务 任务串行执行,任务一个接着一个 会产生堵塞(死锁,崩溃) 主...

  • Synchronized关键字详解2(对非object持锁)

    4.死锁:同步方法容易造成死锁。Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁...

  • iOS中的线程死锁

    线程死锁产生的原因:在一个串行队列的任务中,再向这个队列添加同步任务。典型例子: 我们分析一下: 主队列main_...

网友评论

      本文标题:关于向主线程添加同步任务造成死锁的思考

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