进程(Process)
进程是指在系统中正在运行的一个应用程序,比如同时打开微信和 QQ,系统会分别至少启动 2 个进程。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。进程是系统进行资源分配和调度的一个独立单位。
注意
一个应用程序至少有一个进程,但不是说只有一个进程。
线程(Thread)
线程是某一个进程中一个单独运行的程序。线程是进程的一个实体。线程是 CPU 调度和分派的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源,但是它与同属一个进程的其他线程共享进程所拥有的全部资源。也就是线程存在于进程之中,一个进程由一个或多个线程构成,各线程之间共享相同的代码和全局数据,但各自有自己的堆栈。
一个程序有且只有一个主线程,与用户交互相关的中断性操作都会派发到主线程,程序启动时创建(调用main来启动),主线程的生命周期是和应用程序绑定,程序退出时,主线程也停止。
真正在应用程序中做任务的是线程。
任务(Task)
是指由应用程序完成的一个活动。一个任务既可以是一个进程,也可以是一个线程。任务是一系列为达到某一共同目的的操作集合。
进程和线程的区别和联系
- 线程是CPU和资源调度(执行任务)的基本单位,是程序执行的最小单元,进程是资源分配和调度的基本单位。
- 一个程序可以有多个进程,一个进程可以有多个线程,但至少要有一个线程,而一个线程只能属于一个进程。
- 同一个进程内的线程共享进程的所有资源。
1. NSThread(最底层)
特点:
- 手动管理:需要开发者自行创建、启动和管理线程的生命周期。
- 轻量级:直接操作线程,适合简单场景。
- 可控性高:可以设置线程优先级、名称等属性。
- 缺点:频繁创建线程开销大,需手动处理线程同步问题(如加锁)。
示例:
let thread = Thread(target: self, selector: #selector(task), object: nil)
thread.start()
适用场景:需要精细控制线程行为的场景(较少使用)。
2. GCD(Grand Central Dispatch)(推荐)
特点:
- 基于队列的任务调度:通过队列(Queue)管理任务,自动处理线程生命周期。
- 高效简洁:使用 Block 或闭包封装任务,避免手动管理线程。
- 任务类型:
- 同步执行(
sync
):阻塞当前线程,直到任务完成。 - 异步执行(
async
):不阻塞当前线程。
- 同步执行(
- 队列类型:
- 串行队列(Serial Queue):任务按顺序执行。
- 并发队列(Concurrent Queue):任务并行执行。
- 主队列(Main Queue):串行队列,专用于更新 UI。
- 全局队列(Global Queue):系统提供的并发队列。
示例:
// 异步执行
DispatchQueue.global(qos: .background).async {
// 后台任务
DispatchQueue.main.async {
// 回到主线程更新 UI
}
}
高级功能:
- Dispatch Group:管理多个任务的完成状态。
- Dispatch Semaphore:控制并发数量(如资源竞争)。
- Barrier(屏障任务):确保读写安全。
- Dispatch Source:监听事件(如定时器、文件读写)。
适用场景:绝大多数异步任务,如网络请求、耗时计算。
3. NSOperation 和 NSOperationQueue(基于 GCD 的高级封装)
特点:
- 面向对象:任务被封装为
NSOperation
对象(如NSBlockOperation
)。 - 功能丰富:
- 任务依赖:通过
addDependency
设置执行顺序。 - 取消任务:支持取消单个或所有任务(
cancel()
)。 - 队列控制:设置最大并发数、暂停/恢复队列。
- 任务依赖:通过
- 与 GCD 的区别:
- 更高抽象:适合复杂任务管理(如批量下载)。
- 可控性更强:支持 KVO 监听任务状态。
示例:
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 2 // 最大并发数
let op1 = BlockOperation { /* 任务1 */ }
let op2 = BlockOperation { /* 任务2 */ }
op2.addDependency(op1) // op2 在 op1 完成后执行
operationQueue.addOperations([op1, op2], waitUntilFinished: false)
适用场景:需要任务依赖、取消或复杂调度的场景(如文件分片上传)。
三种方式的对比
特性 | NSThread | GCD | NSOperation |
---|---|---|---|
抽象层级 | 底层(直接操作线程) | 中层(队列管理任务) | 高层(面向对象) |
线程生命周期管理 | 手动管理 | 自动管理 | 自动管理 |
任务依赖 | 不支持 | 需手动实现(如 Barrier) | 原生支持 |
任务取消 | 需手动实现 | 不支持 | 原生支持 |
性能 | 较高 | 高(C 语言 API) | 略低于 GCD(对象开销) |
适用场景 | 简单任务/精细控制线程 | 常见异步任务 | 复杂任务依赖/批量操作 |
在 iOS 中,NSOperation
和 NSOperationQueue
是基于 GCD 的面向对象的高级封装,它们通过抽象化的方式将多线程任务的管理变得更加灵活和可控。以下是它们的核心封装内容:
1. NSOperation 的封装:任务对象化
NSOperation
将“任务”抽象成一个对象(面向对象思想),开发者可以通过子类化或使用系统提供的子类(如 NSBlockOperation
)来定义任务。它的封装包括:
(1)任务封装
- 任务本身:将代码逻辑封装为一个独立的对象(
NSOperation
实例)。 - 任务状态:内部维护了
isReady
、isExecuting
、isFinished
、isCancelled
等状态(通过 KVO 监听)。 - 任务优先级:通过
queuePriority
属性设置优先级(veryLow
、low
、normal
、high
、veryHigh
)。 - 依赖关系:通过
addDependency(_ op: NSOperation)
添加依赖,确保任务按顺序执行。
(2)系统提供的子类
NSBlockOperation
:用闭包(Block)封装任务,支持添加多个闭包(并行执行)。let blockOperation = BlockOperation { print("Task 1") } blockOperation.addExecutionBlock { print("Task 2") }
NSInvocationOperation
(已废弃):通过方法调用封装任务(Swift 中不可用)。
(3)自定义 NSOperation
通过继承 NSOperation
创建自定义任务,需手动管理任务状态:
class CustomOperation: Operation {
override func main() {
guard !isCancelled else { return }
// 任务逻辑
}
}
2. NSOperationQueue 的封装:队列管理
NSOperationQueue
负责管理和调度 NSOperation
任务,封装了线程池、任务调度等底层逻辑,核心功能包括:
(1)自动线程管理
- 基于 GCD:底层使用 GCD 的队列,但通过对象化的方式隐藏了线程细节。
- 线程复用:自动管理线程的创建、复用和销毁,开发者无需手动操作线程。
(2)队列控制
- 最大并发数:通过
maxConcurrentOperationCount
控制并发任务数(设为1
可模拟串行队列)。 - 队列暂停/恢复:通过
isSuspended
属性控制队列是否暂停执行新任务。 - 取消所有任务:通过
cancelAllOperations()
一键取消队列中所有任务。
(3)任务调度
- 依赖管理:自动处理任务之间的依赖关系,确保依赖任务完成后才执行后续任务。
- 优先级调整:结合任务的
queuePriority
和队列的调度策略,动态调整执行顺序。
3. 与 GCD 相比的封装优势
NSOperation
和 NSOperationQueue
在 GCD 的基础上,通过面向对象的方式提供了更高层次的抽象,解决了 GCD 的以下痛点:
(1)任务依赖
- GCD:需手动通过
DispatchGroup
、DispatchSemaphore
或Barrier
实现依赖。 - NSOperation:直接通过
addDependency
声明依赖关系,代码更清晰。let op1 = BlockOperation { /* 任务A */ } let op2 = BlockOperation { /* 任务B */ } op2.addDependency(op1) // op2 依赖 op1 queue.addOperations([op1, op2], waitUntilFinished: false)
(2)任务取消
- GCD:提交到队列的任务无法取消(除非未开始执行)。
- NSOperation:通过
cancel()
方法取消单个任务,或通过cancelAllOperations()
取消队列中所有任务。let operation = BlockOperation { /* 耗时任务 */ } queue.addOperation(operation) operation.cancel() // 取消任务(若未执行)
(3)任务状态监听
- GCD:无原生状态监听机制。
- NSOperation:支持通过 KVO 监听
isFinished
、isCancelled
等状态。operation.addObserver(self, forKeyPath: "isFinished", options: .new, context: nil)
(4)复杂任务管理
- 并发控制:通过
maxConcurrentOperationCount
限制并发数(如最多 3 个下载任务)。 - 优先级调整:动态调整任务的优先级,适应业务需求变化。
4. 底层实现与 GCD 的关系
- NSOperationQueue 底层依赖 GCD:
NSOperationQueue
的线程池和任务调度最终由 GCD 的队列(如DispatchQueue
)实现。 - 更高层次的抽象:
NSOperation
封装了 GCD 的DispatchWorkItem
,并添加了状态管理、依赖等逻辑。
5. 适用场景示例
- 批量下载:限制最大并发数,避免同时下载过多文件。
- 任务依赖:先下载数据,再解析数据,最后更新 UI。
- 可取消任务:用户离开页面时取消所有未完成的任务。
- 动态优先级:用户手动调整某个任务的优先级(如优先下载某个文件)。
总结
NSOperation
和 NSOperationQueue
的封装本质是:
- 将任务对象化:通过
NSOperation
抽象任务,使其具备状态、依赖、优先级等属性。 - 将队列智能化:通过
NSOperationQueue
管理任务调度,隐藏底层线程细节,提供高级控制功能。
这种封装让开发者更专注于业务逻辑,而非多线程的底层实现,尤其适合需要精细控制任务流程的复杂场景。