GCD准确的来讲应该叫做并发编程技术,因为swift3.0后GCD使用方式有很大的变化这里用Swift来重新整理一下GCD。
开发中常见代码
-
同步执行方法,这句话不执行完,就不会执行下一个任务。同步执行不会开启线程。
DispatchQueue.global().sync { print(Thread.current) }
-
异步执行任务,任务没有执行完毕,可以不用等待,异步执行下一个任务 ,具备开启线程的能力! 异步通常又是多线程的代名词!
DispatchQueue.global().async { print(Thread.current) }
-
开发中最常见的情形,在子线程执行完任务后回到主线程更新UI。
DispatchQueue.global().async { print(Thread.current) DispatchQueue.main.async { print(Thread.current) } }
GCD核心概念
将任务添加到队列,指定任务执行的方法。
- 任务
- OC里面用block 封装,swift是闭包。
- 就是一个提前准备好的代码块,在需要的时候执行。
- 队列(负责调度任务)
- 串行队列: 一个接一个的调度任务
- 并发队列: 可以同时调度多个任务
- 任务执行函数(任务都需要在线程中执行!!)
- 同步执行: 不会到线程池里面去获取子线程!
- 异步执行: 只要有任务,会去线程池取子线程(主队列除外)
- Demo
- 串行队列,同步任务
执行结果是不会开启子线程,顺序执行。//创建串行队列 let serialQueue = DispatchQueue(label: "SCGCD", attributes: .init(rawValue: 0)) //同步执行 for i in 0..<10 { serialQueue.sync { print(i) print(Thread.current) } }
- 串行队列,异步任务。
执行结果是会开启子线程,顺序执行。//创建串行队列 let serialQueue = DispatchQueue(label: "SCGCD", attributes: .init(rawValue: 0)) //异步步执行 for i in 0..<10 { serialQueue.async { print(i) print(Thread.current) } }
- 并发队列,异步执行。
执行结果是会开启子线程,不是顺序执行。//创建并发队列 let conQueue = DispatchQueue(label: "Mazy", attributes: .concurrent) //异步执行 for i in 0..<10 { conQueue.async { print(i) print(Thread.current) } }
- 并发队列,同步执行。
执行结果是不会会开启子线程,顺序执行。//创建并发队列 let conQueue = DispatchQueue(label: "KaitoGCD", attributes: .concurrent) //同步执行 for i in 0..<10 { conQueue.sync { print(i) print(Thread.current) } }
- 串行队列,同步任务
- 总结
- 开不开线程,取决于执行任务的函数,同步不开,异步才能开
- 开几条线程,取决于队列,串行开一条,并发可以开多条(异步)
同步任务
利用同步任务,能够做到任务依赖关系,前一个任务是同步任务,这个任务不执行完,队列就不会调度后面的任务。(应用场景是要等待一个任务执行完再去执行其他的任务)
- 例子:
这个代码就会等每次登录操作完成后才是支付和下载。//创建并发队列 let conQueue = DispatchQueue(label: "KaitoGCD", attributes: .concurrent) conQueue.sync { print("用户登录\(Thread.current)") } conQueue.async { print("支付\(Thread.current)") } conQueue.async { print("下载\(Thread.current)") }
全局队列
let globalQueue = DispatchQueue.global()
- 全局队列其实跟并发队列差不多,都是并发,能够调度多个线程。
- 全局队列与并发队列区别
- 1 名称,并发队列取名字,适合于企业开发跟踪错误
- 2 并发队列在MRC情况下需要使用的dispatch_release(q);//ARC 情况下不需要release !
主队列
let globalQueue = DispatchQueue.main
- 主队列其实和串行队列差不多,都是一个一个的执行任务。
- 主队列和串行队列的区别
- 串行队列最多只能开启一条线程,可以是子线程。
- 主队列主要是负责在主线程上执行任务。
- 主队列特性:以FIFO调度任务,如果主线程上有任务在执行,主队列就不会调度任务。这种特性可能会造成一种死锁的情况(面试常问):
- 同步任务死锁:当前是在主线程,让主队列执行同步任务!
以上代码便是死锁的情况,程序会崩溃,因为viewDidLoad()里面执行的任务和DispatchQueue.main.sync执行的任务互相等待造成死锁。override func viewDidLoad() { super.viewDidLoad() print(1) DispatchQueue.main.sync { print(Thread.current) } }
- 同步任务死锁:当前是在主线程,让主队列执行同步任务!
延迟执行
//延迟1秒在子线程中异步执行
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 1) {
print(Thread.current)
}
一次执行
Swift3以后原有的Dispatch once已经被废弃了。这里就用OC来写代码例子,在OC用dispatch_once来写单例还是应用的很多的。(ps:Swift写单例真的比OC方便很多)
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
//苹果推荐使用 gcd 一次执行,效率高
dispatch_once(&onceToken, ^{
//只会执行一次!!
NSLog(@"执行了%@",[NSThread currentThread]);
});
调度组
用一个调度组,可以监听全局队列的任务,主队列去执行最后的任务
//调度组
let group = DispatchGroup()
//添加任务,让队列调度,任务执行情况,最后通知群组
DispatchQueue.global().async(group: group, qos: .default, flags: [], execute: {
print("下载1")
})
DispatchQueue.global().async(group: group, qos: .default, flags: [], execute: {
print("下载2")
})
DispatchQueue.global().async(group: group, qos: .default, flags: [], execute: {
print("下载3")
})
//所有任务执行完毕后,通知
group.notify(queue: DispatchQueue.main) {
print("回到主队列更新UI")
}