GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制,也是目前苹果官方推荐的多线程开发方法。相对于 NSThread 和 NSOperation,GCD抽象层次最高,使用起来也最简单,只是它基于C语言开发,并不像NSOperation是面向对象的开发,而是完全面向过程的。这种机制相比较于前面两种多线程开发方式最显著的优点就是它对于多核运算更加有效。
GCD 中使用队列管理任务, 队列是一种 FIFO 的数据结构。
- 串行队列(Serial Queue):所有任务在一个线程中执行, 并且一个任务执行完毕后,才开始执行下一个任务。
- 并行队列(Concurrent Queue):可以开启多个线程并行执行任务(但不一定会开启新的线程,还要看任务执行方式),并且当一个任务放到指定线程开始执行时,下一个任务就可以开始执行了。
- 主队列(main queue):是一种特殊的串行队列,每个进程至少有一个线程,即主线程,主线程中的任务由主队列管理。刷新UI必须在主线程。
- 全局队列(global queue):系统为我们创建好的一个并行队列,使用起来与我们自己创建的并行队列无本质差别。
- 同步执行(sync):在当前线程执行任务,不会开辟新的线程。必须等到上个任务执行完毕后,才会开始执行。
- 异步执行(async):可以在新的线程中执行任务,但不一定会开辟新的线程。添加到线程启动后即可执行,不必等其它任务执行完成。
串行 | 并行 | 主队列 | |
同步 | 在当前线程,串行执行 | 在当前线程,串行执行 | 死锁 |
异步 | 开辟一个新线程,串行执行 | 可以开辟多个新线程,异步执行 | 在主线程,串行执行 |
一般多任务,耗时操作选用 并行 + 异步;单任务,UI 刷新选用 主队列 + 异步。
会引起死锁的几种情况: 1. 在主线程执行同步操作 DispatchQueue.main.async { print(Thread.current) } 2. 在一个同步线程中,嵌套主线程同步操作 let serial = DispatchQueue(label: "serial") serial.sync { DispatchQueue.main.sync { print(Thread.current) } }
创建队列
//主队列 let mainQueue = DispatchQueue.main //全局队列 let globalQueue = DispatchQueue.global() //串行队列,不指定属性时默认 let serialQueue = DispatchQueue(label: "com.label.serial") //并发队列 let concurrentQueue = DispatchQueue(label: "com.label.concurrent", attributes: .concurrent) /// 自定义并发队列 /// /// - Parameters: /// - label: 标识符 /// - qos: 优先级(quality of service) /// - attributes: 队列属性(类型) /// - autoreleaseFrequency: 自动释放频率 /// - target: let queueInactive = DispatchQueue.init(label: "com.queue.concurent", qos: DispatchQoS.default, attributes: [.concurrent, .initiallyInactive], autoreleaseFrequency: .workItem, target: nil) //触发后才执行 queueInactive.async { print("++++++\(Thread.current)") } //手动触发 queueInactive.activate()
分组队列
func groupAction(_ sender: UIButton) { //DispatchGroup用来管理一组任务的执行,然后监听任务全部完成。比如,多个网络请求同时发出去,等网络请求都完成后reload UI。 let group = DispatchGroup() let concuQueue = DispatchQueue(label: "concu", qos: .default) let itemOne = DispatchWorkItem { print("====\(Thread.current)====") } let itemTwo = DispatchWorkItem { print("====\(Thread.current)====") } let itemThree = DispatchWorkItem { print("====\(Thread.current)====") } concuQueue.async(group: group, execute: itemOne) concuQueue.async(group: group, execute: itemTwo) concuQueue.async(group: group, execute: itemThree) //group.notify会等group里的所有任务全部完成以后才会执行(不管是同步任务还是异步任务)。 group.notify(queue: concuQueue) { print("请求完成") } }
dispatch_once
这个函数在Swift3.0以后的时代已经被删除,因为自从Swift 1开始Swift就已经开始用dispatch_one机制在后台支持线程安全的全局lazy初始化和静态属性。static ,class背后已经在使用dispatch_once了。 Swift 中单例的一种写法 final class SingleTon: NSObject { static let shared = SingleTon() private override init() {} } 使用final,使这个类不能被继承。 设置初始化方法为私有,避免外部对象通过访问init方法创建单例类的实例。
延时执行
func afterAction(_ sender: UIButton) { //GCD可以通过asyncAfter和syncAfter来提交一个延迟执行的任务 let deadline = DispatchTime.now() + 2.0 print("start") DispatchQueue.global().asyncAfter(deadline: deadline) { print("end") } //延迟执行还支持一种模式DispatchWallTime let walltime = DispatchWallTime.now() + 2.0 print("start1") DispatchQueue.global().asyncAfter(wallDeadline: walltime) { print("end1") } //DispatchTime 的精度是纳秒 //DispatchWallTime 的精度是微秒 }
队列的挂起和恢复
//创建并行队列 let conQueue = DispatchQueue(label: "concurrentQueue1", attributes: .concurrent) //暂停一个队列 conQueue.suspend() //继续队列 conQueue.resume()
线程同步
信号量 DispatchSemaphore(value: ):用于创建信号量,可以指定初始化信号量计数值,这里我们默认1. semaphore.wait():会判断信号量,如果为1,则往下执行。如果是0,则等待。 semaphore.signal():代表运行结束,信号量加1,有等待的任务这个时候才会继续执行。 //获取系统存在的全局队列 let queue = DispatchQueue.global(qos: .default) //创建一个信号量,初始值为1 let semaphore = DispatchSemaphore(value: 1) for i in 1...10 { queue.async { //永久等待,直到Dispatch Semaphore的计数值 >= 1 semaphore.wait() print("\(i)") //发信号,使原来的信号计数值+1 semaphore.signal() } }参考: iOS开发系列--并行开发其实很容易