美文网首页
多线程基础知识+17个字判别死锁

多线程基础知识+17个字判别死锁

作者: GDCoder | 来源:发表于2021-04-21 10:01 被阅读0次

    我们在面试的时候经常会遇到很多关于多线程的面试题,这些面试题的答案你都知道吗?

    1.你理解的多线程是什么?

    2.iOS的多线程方案有哪些?你更倾向于哪一种?为什么?

    3.你在项目中用过GCD吗?有哪些使用?

    4.GCD的队列类型有哪些?

    5.什么情况下会产生死锁?

    6.说一下OperationQueue和GCD的区别,以及各自的优势是什么?

    7.线程安全的处理手段有哪些?

    8.OC你了解的锁有哪些?在你回答的基础上进行二次提问;

    追问一:自旋锁和互斥锁对比?

    追问二:使用以上锁需要注意哪些?

    追问三:用C/OC/C++,任选其一,实现自旋或互斥锁,口诉即可.

    .....

    相信以上的前面的几个问题,大家都是很容易回答出来的,但是后面的关于锁的问题,我们可能用得比较少,比如加锁,解锁、自旋锁、互斥锁等等,今天就让我们一起了解这些东西,面试官问到多线程,那就是随便问,我们也能随便答.首先我们简单来回顾一下基础的知识.

    iOS多线程有哪些?

    pthread:是一个通用的多线程API,适用于Unix\Linux\Windows等多个系统,可以跨平台,但是由于它的语言是C语言,而且内存是我们程序员自己管理,所以使用难度比较大,而且几乎是不用的.

    NSThread:使用起来更加面向对象,简单易用,可以直接操作线程对象,它的使用语言是OC,它的内存也是由我们程序员自己管理,所以偶尔使用

    GCD:旨在替换NSThread等线程技术,它的一个优点可以充分利用设备的多核,它的语言是C语言,线程的生命周期是自动管理,所以用起来比较方便,所以经常被使用

    NSOperation:它是基于GCD(底层是GCD),它比GCD多了一些更简单实用的功能,使用起来更加面向对象,它的语言也是OC,线程的生命周期也是自动管理,所以用起来也比较方便,也是经常被使用

    下面用一张图,放在一起对比,看着比较明显和好记:

    再补充一点:NSThread、GCD、NSOperation底层都是基于pthread.

    我们用得比较多的是用GCD,我自己也是喜欢用GCD.下面就来介绍一下

    GCD的常用函数有哪些?

    1.用同步的方式来执行任务:

    dispatch_sync(queue, block);    queue:队列,  block:任务.

    2.用异步的方式来执行任务:

    dispatch_async(queue, block);    queue:队列,  block:任务.

    number=1是代表主线程,number=不是1就是子线程.下面是源码下载地址(网络不好的可能要开vpn才能打开)

    GCD源码下载地址

    GCD的队列

    GCD的队列可以分为2大类型

    1.并发队列 (Concurrent Dispatch Queue)

    可以让多个任务并发 (同时) 执行 (自动开启多个线程同时执行任务)

    并发功能只有在异步 (dispatch_async)函数下才有效

    2.串行队列 ( Serial Dispatch Queue)

    让任务一个接一个地执行 (一个任务执行完毕后,再执行下一个任务)

    同步、异步、并发、串行 4个术语的区分

    同步和异步主要影响:能不能开启新的线程

    同步:在当前线程中执行任务,不具备开启新线程的能力

    异步:在新的线程中执行任务,具备开启新线程的能力

    并发和串行主要影响:任务的执行方式

    并发:多个任务并发(同时)执行

    串行:一个任务执行完毕后,再执行下一个任务

    有个小提问:

    dispatch_async(queue, block)在执行任务的时候,一定开启新的线程吗?

    答案是否定的,因为我们知道有个特殊的串行队列,就是主队列,只要是主队列,不管你是同步还是异步线程,都是在当前线程执行,不会开启新的线程,请看下图:

    这样的话就有同步、异步情况下有手动创建的串行、并发、主队列组合6种情况.

    请看下图:

    接下来,我们就说一下,面试题中常见的线程死锁问题.

    什么情况下会产生死锁?

    所谓死锁就意味着卡住了,这个线程不会往下执行了.请问以下代码会不会产生死锁:

    (案例1我会仔细解释,后面的死锁案例就不详细解释了,道理都是一样的,下面一句话总结)

    案例一:

    请问上面的案例会不会出现死锁?答案是会的,首先队列的特点是:FIFO,first in first out,先进先出.因为我们知道dispatch_sync是同步方法,它不会开启新的线程,所以它肯定也是在主线程执行的,所以任务1执行完了,肯定执行任务2,而任务3在等任务2执行完毕;

    同样的queue是主队列,而任务2是放在主队列里面的,而任务2主队列前面是viewDidLoad方法,所以任务2会等viewDidLoad执行完,也就是任务2会等任务3执行完才会执行,那就很明显了着2个任务相互等待造成死锁!

    我们看一下运行结果:

    直接崩溃.如果用画图来解释一下,也是比较清晰,请看下图

    所以看图就很清晰,想取出任务2必须viewDidLoad执行完毕,viewDidLoad执行完毕必须任务3执行完毕,而任务3执行完毕,必须要执行完sync,而sync又取不出来任务2,所以造成死锁.我们也可以证明任务2只要是主队列中,它肯定是在viewDidLoad后面执行.我们只要把上面的同步改成异步即可,请看下图:

    案例二:

    这个会不会死锁?答案是会死锁.首先block0和block1都是在同一个queue中,block1在后面,而block1是同步的dispatch_sync所以会等block0执行完,再执行block1,所以是任务3等任务4;而dispatch_sync是同步的,任务4又会等任务3执行完!相互等待,死锁!

    看一下执行结果:

    案例三:

    不会死锁

    案例四:

    不会死锁

    从上面的例子,大家是不是觉得:只要是同步的,而且往同一个队列加任务就会死锁呢?

    那么请看下面的案例

    案例五:

    当前就是同步的,而且往同一个队列加任务!此时是不会产生死锁.队列必须也是串行队列才是死锁!

    所以通过以上的案例,我们得出以下总结,只要记住这句话,我们就知道什么情况下会出现死锁.

    死锁总结:

    使用sync函数往当前串行队列中添加任务

    会卡住当前的串行队列(产生死锁)

    注意条件:

    1.sync函数

    2.当前串行队列:同一个队列而且是串行队列

    知道这个结论我们就可以知道什么时候产生死锁!辨别死锁就非常简单,上面的案例都适用,大家可以自己看下.

    队列组的使用

    比如我们现在有一个需求是:异步并发执行任务1、任务2;等任务1、任务2都执行完毕后,再回到主线程执行任务3,这个时候我们就用到GCD的一个队列组的应用.

    这里任务3也可以精简写,直接把queue改成main_queue就可以;还有个就是我们还可以添加任务4,也是和任务3一样的写法,那么任务3和任务4就会等任务1和任务2执行完以后,交替执行!

    由于多线程内容较多,我会在接下来的博客继续介绍!(如:多线程的其他面试题、多线程所有的锁对比及使用等).

    接下来我会继续努力编写其他博客,您的支持就是我最大的动力!

    如果觉得我写得对您有所帮助,请点赞关注我,我会持续更新😄

    感谢支持🙏🙏🙏!

    相关文章

      网友评论

          本文标题:多线程基础知识+17个字判别死锁

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