GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制,也是目前苹果官方推荐的多线程开发方法。相对于 NSThread 和 NSOperation,GCD抽象层次最高,使用起来也最简单,只是它基于C语言开发,并不像NSOperation是面向对象的开发,而是完全面向过程的。这种机制相比较于前面两种多线程开发方式最显著的优点就是它对于多核运算更加有效。
GCD 中使用队列管理任务, 队列是一种 FIFO 的数据结构。
- 串行队列(Serial Queue):所有任务在一个线程中执行, 并且一个任务执行完毕后,才开始执行下一个任务。
- 并行队列(Concurrent Queue):可以开启多个线程并行执行任务(但不一定会开启新的线程,还要看任务执行方式),并且当一个任务放到指定线程开始执行时,下一个任务就可以开始执行了。
两个特殊队列:
- 主队列(main queue):是一种特殊的串行队列,每个进程至少有一个线程,即主线程,主线程中的任务由主队列管理。刷新UI必须在主线程。
- 全局队列(global queue):系统为我们创建好的一个并行队列,使用起来与我们自己创建的并行队列无本质差别。
说完队列,相应的,任务除了管理,还得执行。在GCD中并不能直接开辟线程执行任务,所以在任务加入队列之后,GCD给出了两种执行方式:
- 同步执行(sync):在当前线程执行任务,不会开辟新的线程。必须等到上个任务执行完毕后,才会开始执行。
- 异步执行(async):可以在新的线程中执行任务,但不一定会开辟新的线程。添加到线程启动后即可执行,不必等其它任务执行完成。
一般多任务,耗时操作选用 并行 + 异步;单任务,UI 刷新选用 主队列 + 异步。
// 主线程同步操作,引起死锁
dispatch_sync(dispatch_get_main_queue(), ^{
print(“”);
});
创建队列
//主队列
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()
}
}