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

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

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个字判别死锁)