多线程 - 原理机制
线程与进程
线程定义
- 线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行
- 进行要执行任务,必须得有线程,进行至少有一条线程
- 程序启动会默认开启一条线程,这条线程被称为主线程/UI线程
进程定义
- 进程是系统进行资源和高度的基本单位
- 在移动端进程是指在系统中正在运行的一个程序
- 每个进程之间是独立的,每个进程均运行在专用的且受保护的内存中
进程与线程的关系
- 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间
- 资源拥有:同一进程内的线程共享本进程的资源内存、I/O、CPU等,但是进程之间的资源是独立的
- 相互影响:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃后整个进程就死掉了,所以多进程要比多线程健壮
- 资源占用:进程切换时资源消耗大,效率高;所以涉及到频繁切换时,使用线程要好于进程,同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程
- 执行过程:每个独立的进程有一个程序入口、顺序执行序列和程序出口。但是线程不能独立执行,必须存在应用程序中(进程),有应用程序提供多个线程执行控制(多线程开发)
- 线程是处理器调试的基本单位,但进程不是,线程也是进行执行的基本单元
线程与进程的关系
- 多线程中的队列有:串行队列,并发队列,全局队列,主队列
- 队列可以理解为一种数据结构,以某种方式,等待线程去执行
iOS线程与RunLoop的关系
- 线程与RunLoop是一一对应的,一个RunLoop对应一个核心线程,为什么说是核心线程,因为RunLoop是可以嵌套的,但是核心只有一个,他们的对应关系保存在一个全局字典里
- RunLoop是用来管理线程的,线程执行完任务时会进入休眠状态,有任务进来时会被唤醒,开始执行任务,所以说RunLoop是事件驱动的
- RunLoop在第一次获取时被创建,线程结束时被销毁,主线程的RunLoop在程序启动的时候就会被创建
- 子线程的RunLoop是懒加载的,只有在使用的时候才会被创建
注:在子线程使用NSTimer时要注意确保子线程的RunLoop已经创建,否则NSTimer不会生效
多线程
定义
- 多线程是一个进程中并发多个线程同时执行各自的任务,就是由单核CPU通过时间片不断地切换执行任务
原理
- 同一时间,CPU只能处理一条线程,只有一条线程在执行(工作)
- 多线程并发(同时)执行,其实是CPU快速地在多条线程之间切换(调度)
- 如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
多线程的优缺点
优点:
- 减少应用程序的堵塞,增加程序的执行效率
- 适应提高CPU和内存的利用率
- 线程上的任务执行完成后,线程自动销毁
缺点:
- 线程的开启需要占用一定的内存空间,默认是512kb/线程
- 线程开启的越多内存占用越大,会降低程序的性能
- 线程越多CPU在调用线程上的开销就越大
- 程序设计更加复杂,需要考虑线程间的通信,多线程的数据共享问题
iOS多线程常见的技术方案
| 方案 | 简介 | 语言 | 线程生命周期 | 使用频率 |
|---|---|---|---|---|
| pthread | 一套通用的多线程API 适用于Unix/Linux/Windows等系统 跨平台、可移植 使用难度大 | C | 程序员管理 | 几乎不用 |
| NSThread | 使用更加面向对象 | 简单易用、可直接操作线程对象 | OC | 程序员管理 |
| GCD | 旨在替代NSThread等线程计数 充分利用设备的多核 | C | 自动管理 | 经常使用 |
| NSOperation | 基于GCD(底层实现是GCD) 比GCD多了一些更简单实用的功能 使用更加面向对象 | OC | 自动管理 | 经常使用 |
GCD
全称
Grand Central Dispatch,由C语言实现,是苹果为多核的并行运算提出的解决方案,GCD会自动利用更多的CPU内核,自动管理线程的声明周期,程序员只需要告诉GCD需要执行的任务,无需编写任何管理线程的代码。GCD也是iOS使用频率最高的多线程技术。
优点:
- GCD 可用于多核的并行运算
- GCD 会自动利用更多的CPU内核(比如双核,四核)
- GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
GCD任务和队列
任务
任务就是执行操作的意思,也就是在线程中执行的那段代码。
在GCD中是放在block中的。
- 执行任务有两种方式:[同步执行]和[异步执行]
- 两者的区别:是否等待队列的任务执行结束,以及是是否具备开启新线程的能力
同步执行(sync):
- 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行
- 只能在当前线程中执行任务,
不具备开启新线程的能力
异步执行(async):
- 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务
- 可以在新的线程中执行任务,
具备开启新线程的能力
队列
队列是一种特殊的线性表,采用FIFO(先进先出)的原则。
- GCD中有两种队列:[串行队列]和[并发队列],两者都符合FIFO原则
- 两者的区别:执行顺序不同,以及开启线程数不同
串行队列(Serial Dispatch Queue):
- 每次只有一个任务被执行,让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
并发队列(Concurrent Dispatch Queue):
- 可以让多个任务并发(同时)执行,(可以开启多个线程,并且同时执行任务)
注:并发队列的并发功能只有在异步(dispatch _async)方法下才有效
GCD的常用函数
- GCD中有2个用来执行任务的函数
- 用同步的方式执行任务
1
2
3dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:队列
block:任务 - 用异步的方式执行任务
1
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
- 用同步的方式执行任务
各种队列的执行效果
| 并发队列 | 手动创建的串行队列 | 主队列 | |
|---|---|---|---|
| 同步(sync) | 没有开启新线程 串行执行任务 |
没有开启新线程 串行执行任务 |
没有开启新线程 串行执行任务 |
| 异步(async) | 有开启新线程 并发执行任务 |
有开启新线程 串行执行任务 |
没有开启新线程 串行执行任务 |
使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)
GCD队列组的使用
- 异步并发执行任务1,任务2
- 待任务1,任务2都执行完毕后,再回到主线程执行任务3
- 待任务1,任务2都执行完毕后,回到子线程,并发执行任务5和任务6
1 | // 创建队列组 |
参考
李明杰老师课件- objc源码
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ivan's Blog!
评论







