下面是:2个并发进程、和2个并发线程的示意图:
下面介绍三种多线程技术(Thread、Cocoa Operation、Grand Central Dispatch):
1、最轻量级Thread(需要自己管理线程的生命周期和同步,所以不常用):线程同步对数据的加锁会导致一定的系统开销。但偶尔也会有一定的用处,如Thread.current可以获得当前线程,这种使用就很方便。
//下面是创建Thread的两种方法
//方法一:使用类方法,创建线程(创建好,就启动了)
Thread.detachNewThreadSelector(#selector(addOne), toTarget: self, with: nil)
//方法二:线程实例化,采用便利构造器
let thread2 = Thread(target: self, selector: #selector(addTwo), object: nil)
thread2.start()
2、Cocoa Operation(相关类Operation和OperationQueue):这种方法就不需要关心线程管理和数据同步,只需把重点放在多线程分别要执行的任务。
let blockOne = BlockOperation(block: addOne)
let blockTwo = BlockOperation(block: addTwo)
let queue = OperationQueue()
queue.addOperation(blockOne)
queue.addOperation(blockTwo)
3、Grand Central Dispatch(GCD):
GCD会自动合理地利用更多的CPU内核(即根据系统负载来自动增减线程数量,从而减少了上下文切换,而增加计算效率)、自动管理线程的生命周期(如创建线程、调度任务、自动同步,无需加锁、销毁线程等,使用就不用过多关心这些与处理事物无关的代码),所以方便的GCD是我们学习iOS多线程的重点。
使用GCD就是使用dispatch queue(调度队列)的对象,用来接受任务并执行,而且与以上两种方法不同的是,dispatch queue是可以并发的,也可以FIFO串行。
GCD有两种队列(串行、并行,一个队列对象就代表一个线程),用于存放任务,任务的执行也有两种同步、异步执行。具体如何执行,理解下图:
创建队列:
主队列:主线程中的唯一队列,用于刷新UI(因为主线程主队列是唯一的,所以这种操作也是串行的),所以其他的耗时任务要放到其他线程的队列中执行。获得主队列:let mainQueue = DispatchQueue.main
自定义队列:
对于并行队列,有4个常用的执行优先级(由高到低):.userInitiated—>.default—>.utility—>.background,一般最后一个就用于执行不太关心、极其费时的后台任务。
- 创建串行队列:let serial = DispatchQueue(label: "serialQueue"),一个串行队列之中,任务是FIFO的,但是可以创建多个串行队列,而串行队列之间是并发的。
- 创建并行队列(对于并发任务,一般就用系统提供的全局并行队列):let conflict = DispatchQueue(label: "conflictQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
- 全局并行队列:let globalQueue = DispatchQueue.global(qos: .default),qos就是优先级。
然后就是创建任务了:
同步任务:在任务执行时,线程的队列会等待执行完成后,才执行下一个任务,但是却可以追加新的任务(虽然没有立即执行:阻塞)
let globalQueue = DispatchQueue.global(qos: .default)
globalQueue.sync {
addOne()
}
异步任务:不会阻塞当前线程
let globalQueue = DispatchQueue.global(qos: .default)
globalQueue.async {
self.addOne()
}
let mainQueue = DispatchQueue.main
mainQueue.async {
self.addTwo() //主线程先执行
}