iOS多线程概念总结
Posted Mr_yu__
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS多线程概念总结相关的知识,希望对你有一定的参考价值。
前言
在上一篇章中我们主要探索了KVO的底层原理,和实现过程,也学习了自定义KVO的实现,那么本篇就开启了大家心心念念的多线程了,其实多线程相关的使用方式,和一些概念的文章并不少,也是面试中基本是必问的内容,那么今天我们就一起来总结一些多线程的一些基本概念,权当回顾一下😄
1.线程的定义
-
a.线程是进程的基本执⾏单元,⼀个进程的所有任务都在线程中执⾏
-
b.进程要想执⾏任务,必须得有线程,进程⾄少要有⼀条线程
-
c.程序启动会默认开启⼀条线程,这条线程被称为主线程或UI线程
2.进程的定义
-
a.进程是指在系统中正在运⾏的⼀个应⽤程序
-
b.每个进程之间是独⽴的,每个进程均运⾏在其专⽤的且受保护的内存空间内
-
c.通过活动监视器(全局搜索活动监视器即可)可以查看Mac系统中所开启的进程
3.两者的关系
地址空间:同⼀进程的线程共享本进程的地址空间,⽽进程之间则是独⽴的地址空间。
资源拥有:同⼀进程内的线程共享本进程的资源,如内存、I/O、cpu等,但是进程之间的资源是独⽴的。
-
a.⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进程都死掉。所以多进程要⽐多线程健壮。
-
b.进程切换时,消耗的资源⼤,效率⾼。所以涉及到频繁的切换时,使⽤线程要好于进程。同样如果要求同时进⾏并且⼜要共享某些变量的并发操作,只能⽤线程不能⽤进程。
-
c.执⾏过程:每个独⽴的进程有⼀个程序运⾏的⼊⼝、顺序执⾏序列和程序⼊⼝。但是线程不能独⽴执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制。
-
d.线程是处理器调度的基本单位,但是进程不是。
-
e.线程没有地址空间,线程包含在进程地址空间中。
4.多线程的原理
如图所示,多线程的原理其实就是CPU快速的在多个线程之间进行快速切换,而并不是我们想象的多个线程同时执行,只不过因为CPU在切换调度线程的速度很快,导致我们感觉像是在同时执行,也因此,如果线程数过多就会非常消耗CPU资源,影响执行效率,在我们现实开发中,一般也会针对耗时的操作,另开线程进行处理,从而避免主线程的阻塞
总结:
-
时间片:CPU在多个任务直接进⾏快速的切换,这个时间间隔就是时间⽚。(单核CPU)同⼀时间,CPU只能处理1个线程,换⾔之,同⼀时间只有 1 个线程在执⾏
-
多线程同时执⾏:是CPU快速的在多个线程之间的切换,CPU调度线程的时间⾜够快,就造成了多线程的同时执⾏的效果
-
如果线程数⾮常多,CPU会在N个线程之间切换,消耗⼤量的CPU资源,每个线程被调度的次数会降低,线程的执⾏效率降低
多线程技术方案
5.多线程的优缺点
优点
-
a.能适当提⾼程序的执⾏效率
-
b.能适当提⾼资源的利⽤率(如CPU,内存)
-
c.线程上的任务执⾏完成后,线程会⾃动销毁
缺点
-
a.开启线程需要占⽤⼀定的内存空间(默认情况下,每⼀个线程都占512KB)
-
b.如果开启⼤量的线程,会占⽤⼤量的内存空间,降低程序的性能
-
c.线程越多,CPU在调⽤线程上的开销就越⼤
-
d.程序设计更加复杂,⽐如线程间的通信、多线程的数据共享
6.线程的生命周期
New:就是刚通过创建出来的线程;
Runnable:就是调用的线程的start方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
Running:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
Blocked:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep、等待同步锁,线程就从可调度线程池移出,处于了阻塞状态,这个时候sleep到时、获取同步锁,此时会重新添加到可调度线程池。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
Dead:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源。
7.线程池
执行步骤对应如下:
-
1.当有任务进来时,线程池先判断如果正在运行的线程数量小于核心线程数,那么马上创建核心线程运行这个任务;
-
2.如果正在运行的线程数量大于或等于核心线程数,那么将这个任务放入队列;
-
3.如果这时候队列满了,而且正在运行的线程数量小于最大线程数,那么还是要创建非核心线程立刻运行这个任务;
-
4.如果队列满了,而且正在运行的线程数量大于或等于最大线程数,那么线程池饱和策略将进行处理。
饱和策略
-
1.AbortPolicy直接抛出RejectedExecutionExeception异常来阻⽌系统正常运⾏
-
2.CallerRunsPolicy将任务回退到调⽤者
-
3.DisOldestPolicy丢掉等待最久的任务
-
4.DisCardPolicy直接丢弃任务
这四种拒绝策略均实现的RejectedExecutionHandler接⼝
8.互斥锁与自旋锁
在多线程中,因为在同一个进程中,资源是共享的,所以多线程必然存在着资源的竞争,那么就引入了锁的概念。
互斥锁
最常使用于线程同步的锁;标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁;临界区和互斥量都可用来实现此锁,通常情况下锁操作失败会将该线程睡眠等待锁释放时被唤醒。
互斥锁小结
-
1.保证锁内的代码,同一时间,只有一条线程能够执行
-
2.互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差
互斥锁参数
-
1.能够枷锁的任意NSObject对象
-
2.锁对象一定要保证所有线程都能够访问
-
3.如果代码中只有一个地方需要枷锁,大多都使用self,这样可以避免单独再创建一个锁对象
自旋锁
一种用于保护多线程共享资源的锁,与一般互斥锁不同之处在于当自旋锁尝试获取锁时以忙等待的形式不断地循环检查锁是否可用。当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会一直等待(不会睡眠),当上一个线程的任务执行完毕,下一个线程会立即执行。
区别(取自同期优秀LGPerson总结😄)
自旋锁会忙等,所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释放锁。
互斥锁会休眠,所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时cpu可以调度其他线程工作,直到被锁资源释放锁。此时会唤醒休眠线程。
自旋锁优缺点
优点在于,因为自旋锁不会引起调用者睡眠,所以不会进行线程调度,CPU时间片轮转等耗时操作。所有如果能在很短的时间内获得锁,自旋锁的效率远高于互斥锁。
缺点在于,自旋锁一直占用CPU,他在未获得锁的情况下,一直运行自旋,所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。自旋锁不能实现递归调用。
9.atomic与nonatomic 的区别
1.OC在定义属性时有nonatomic和atomic两种选择,默认为atomic属性
- atomic:原子属性,为setter方法加自旋锁(即为单写多读)
- nonatomic:非原子属性,不会为setter方法加锁
2.nonatomic和atomic的对比
- atomic:线程安全,需要消耗大量的资源;
- nonatomic:非线程安全,适合内存小的移动设备。
ios开发的建议
- 如非需抢占资源的属性(如购票,充值),所有属性都声明为nonatomic。
- 尽量避免多线程抢夺同一块资源。
- 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
10.线程和Runloop的关系
-
1.runloop与线程是⼀⼀对应的,⼀个runloop对应⼀个核⼼的线程,为什么说是核⼼的,是因为runloop是可以嵌套的,但是核⼼的只能有⼀个,他们的关系保存在⼀个全局的字典⾥。
-
2.runloop是来管理线程的,当线程的runloop被开启后,线程会在
执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务 -
3.runloop在第一次获取时被创建,在线程结束时被销毁
-
4.对于主线程来说,runloop在程序一启动就默认创建好了
-
5.对于子线程来说,runloop是懒加载的,只有当我们使用的时候才会创建,所以在子线程用定时器要注意:确保子线程的runloop被创建,不然定时器不会回调
以上是关于iOS多线程概念总结的主要内容,如果未能解决你的问题,请参考以下文章
iOS多线程知识总结/队列概念/GCD/串行/并行/同步/异步
iOS多线程总结——NSOperation与NSOperationQueue的使用