iOS多线程详解(一)--- 多线程基础
前言
最近想系统的研究下iOS上的多线程,就搜集了大量的资料,在这些资料的基础之上形成了这篇文章。一方面希望自己加深下印象,另外一方面也希望对他人提供一些帮助。由于本人水平有限,难免出现错误和疏漏之处,还请读者见谅。本篇文章主要参考了李明杰老师的教程和一些优秀的文章,再此深表感谢。
1、基本概念
-
进程
进程是指在系统中正在运行的一个应用程序。例如,同时打开QQ和Xcode,系统就会分别启动2个进程。在MAC电脑上,可以通过活动监视器查看进程。
活动监视器.png -
线程
一个进程想要执行任务,必须得有线程(每1个进程至少要有1条线程)。线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。例如,使用网易云音乐播放音乐、使用迅雷下载电影等都需要在线程中进行。 -
主线程
处理UI,所有更新UI的操作都必须在主线程上执行。不要把耗时操作放在主线程,会卡界面。 -
线程的串行
一个线程中的任务的执行是串行的。如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。 -
多线程
一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。进程类似于实际生活中的车间,而线程可以理解为车间中的工人。
2、线程的状态与生命周期
下图是线程状态示意图,从图中可以看出线程的生命周期是:新建-就绪-运行-阻塞-死亡
线程状态.png
下面分别阐述线程生命周期中的每一步
- 新建:实例化线程对象
- 就绪:向线程对象发送start消息,线程对象被加入可调度线程池等待CPU调度。
- 运行:CPU负责调度可调度线程池中线程的执行。线程执行完成之前,状态可能会在就绪和运行之间来回切换。就绪和运行之间的状态变化由CPU负责,程序员不能干预。
- 阻塞:当满足某个预定条件时,可以使用休眠或锁,阻塞线程执行。sleepForTimeInterval(休眠指定时长),sleepUntilDate(休眠到指定日期),@synthesized(self):(互斥锁)
- 死亡:正常死亡,线程执行完毕。非正常死亡,当满足某个条件后,在线程内部中止执行/在主线程中止线程对象。
- 线程的exit和cancel:
[NSThread exit]:一旦强行终止线程,后续的所有代码都不会被执行。
[thread cancek]:并不会直接取消线程,只是给线程对象添加isCancelled标记。
3、多线程的原理
在同一时间内,(单核)CPU只能处理1条线程,只有1条线程在工作(执行)。因此,多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)。如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。
思考:如果线程非常非常多,会发生什么情况?
1、CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源
2、每条线程被调度执行的频次会降低(线程的执行效率降低)
4、多线程的优缺点
-
多线程的优点
1、能适当提高程序的执行效率
2、能适当提高资源利用率(CPU、内存利用率) -
多线程的缺点
1、开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存,降低程序的性能
2、线程越多,CPU在调度线程上的开销就越大
3、程序设计会更加复杂(比如线程之间的通信、多线程的数据共享)
5、多线程在iOS开发中的应用
-
什么是主线程
一个iOS程序运行后,默认会开启1条线程,称之为“主线程”或“UI线程” -
主线程的主要作用
1、显示/刷新UI界面
2、处理UI事件(比如点击事件、滚动事件、拖拽事件等) -
主线程的使用注意
1、不要将比较耗时的操作放到主线程中,因为耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验。
2、将耗时操作放在子线程(后台线程、非主线程)中执行
6、多线程的四种解决方案
多线程的四种解决方案分别是:pthread,NSThread,GCD,NSOperation。
下图给出了这四种方案的解读和对比。
多线程的四种解决方案.png
7、线程安全问题
当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。就好比几个人在同一时修改同一个表格,造成数据的错乱。
解决多线程安全问题的方法
- 方法一:互斥锁(同步锁)
@synchronized(锁对象) {
// 需要锁定的代码
}
判断的时候锁对象要存在,如果代码中只有一个地方需要加锁,大多都使用self作为锁对象么这样可以避免单独再创建一个锁对象。
加了互斥锁的代码,当新的线程访问时,如果发现其他线程正在执行锁定的代码,新线程就会进入休眠。
- 方法二:自旋锁
加了自旋锁,当新线程访问代码时,如果发现有其他线程正在锁定代码,新线程就会用死循环的方式,一直等待锁定的代码执行完成。相当于不停尝试执行代码,比较消耗性能。
属性修饰atomic本身就有一把自旋锁。
下面说明了属性修饰nonatomic和atomic
nonatomic 非原子属性,同一时间可以有很多线程读和写
atomic 原子属性(线程安全),保证同一时间只有一个线程能够写入(但是同一个时间多个线程都可以取值),atomic 本身就有一把锁(自旋锁)
atomic:线程安全,需要消耗大量的资源
nonatomic:非线程安全,不过效率更高,一般使用nonatomic
8、小结
本节主要介绍了多线程的基础知识,从下节开始介绍iOS多线程的具体使用。
相关系列文章
iOS多线程详解(一)--- 多线程基础
iOS多线程详解(二)--- pthread&NSThread
iOS多线程详解(三)--- GCD
iOS多线程详解(四)--- NSOperation
iOS多线程详解(五)--- 线程安全(锁的创建)
iOS多线程详解(六)--- 线程安全(Property)
参考资料:
1、李明杰老师的iOS视频教程
2、http://www.cocoachina.com/ios/20170707/19769.html