参考网址:http://www.hangge.com/blog/cache/detail_745.html
https://blog.youkuaiyun.com/wnnvv/article/details/80942156
Grand Central Dispath-GCD是Apple开发的一个多核编程的解决方法,基本概念就是dispatch queue(调度队列),queue是一个队列,它接收任务,并将任务以先到先执行的顺序来执行。GCD的底层是用线程实现。
一. GCD的三种队列(用户队列、全局队列、主队列):
首先,Dispatch Queue执行任务可以是并发或串行的,但Main Dispatch Queue一定是串行的,因为它是在主线程执行的。
#串行serial & 并行concurrent
- 串行队列:按添加顺序一个一个执行任务,前一个执行完下一个才能执行;
- 并行队列:一个或多个任务一起开始执行;
1. 自己创建队列 ->
///自己创建的并行队列
let queueConcurrent = DispatchQueue(label: "queueConcurrent", qos: .default, attributes: .concurrent)
///自己创建的串行队列
let queueSerial = DispatchQueue(label: "queueSerial", qos: .default)
2. 获取系统存在的全局队列Global Dispatch Queue ->
let queueGlobal = DispatchQueue.global(qos: .default)//默认是并行队列
3. 运行在主线程的Main Dispatch Queue ->
let queueMain = DispatchQueue.main//一定是串行队列
除了Main Dispatch Queue外,Dispatch Queue有6个执行优先级(qos):
.userInteractive 非常高
.userInitiated 高
.default 正常
.utility 低
.background 非常低的优先级(这个优先级只用于不太关心完成时间的真正的后台任务)
.unspecified 非常非常低
二. GCD添加任务到队列的两种方法 :
- async异步追加Block块(async函数不做任何等待,即把任务加进队列中就马上开始执行)
- sync同步追加Block块(按顺序执行任务)
#同步sync & 异步async(线程概念)
- 同步sync:不会开启新的线程,会阻塞当前的线程在这个线程里执行任务。
- 异步async:可以开启新的线程(在主线程列队中不开启新的线程)。不会阻塞当前线程,会选择在恰当的时机在当前线程或者另开线程执行任务(看系统如何调度),开始任务和完成任务时间是不确定的。
三. 用代码来解析同步与异步&串行与并行&死锁
///自己创建的并行队列
let queueConcurrent = DispatchQueue(label: "queueConcurrent", qos: .default, attributes: .concurrent)
///自己创建的串行队列
let queueSerial = DispatchQueue(label: "queueSerial", qos: .default)
· 串行队列异步执行:
如下,串行队列中的两个任务与主线程中的任务异步执行。因为是异步执行,串行队列中的任务不会阻塞线程,主线程的任务不用等串行队列中的任务完成就已经开始执行了。而串行队列中的两个任务是按顺序执行,第二个任务要等第一个任务执行完后才开始执行。
override func viewDidLoad() {
super.viewDidLoad()
queueSerial.async {
for _ in 0 ... 3 {
print("queue1")
}
}
queueSerial.async {
for _ in 0 ... 3 {
print("queue2")
}
}
print("mainThreadWork1")
print("mainThreadWork2")
print("mainThreadWork3")
}
打印结果:
queue1
mainThreadWork1
queue1
mainThreadWork2
queue1
mainThreadWork3
queue2
queue2
queue2
· 并行队列异步执行:
如下,并行队列中的两个任务与主线程中的任务异步执行。因为是异步执行,并行队列中的任务不会阻塞线程,主线程的任务不用等并行队列中的任务完成就已经开始执行了。而并行队列中的两个任务也是一起开始执行,不用等另一个任务执行完成才开始。
override func viewDidLoad() {
super.viewDidLoad()
queueConcurrent.async {
for _ in 0 ... 3 {
print("queue1")
}
}
queueConcurrent.async {
for _ in 0 ... 3 {
print("queue2")
}
}
print("mainThreadWork1")
print("mainThreadWork2")
print("mainThreadWork3")
}
打印结果:
mainThreadWork1
queue1
queue2
mainThreadWork2
queue1
queue2
mainThreadWork3
queue1
queue2
· 并行队列同步执行:
如下,并行队列中的两个任务与主线程中的任务同步执行。因为是同步执行,队列中执行任务的线程会阻塞,主线程的任务要等并行队列中的任务完成后才能开始执行。而并行队列中的第二个任务也要等第一个任务执行完后才能开始执行。
override func viewDidLoad() {
super.viewDidLoad()
queueConcurrent.sync {
for _ in 0 ... 3 {
print("queue1")
}
}
queueConcurrent.sync {
for _ in 0 ... 3 {
print("queue2")
}
}
print("mainThreadWork1")
print("mainThreadWork2")
print("mainThreadWork3")
}
打印结果:
queue1
queue1
queue1
queue2
queue2
queue2
mainThreadWork1
mainThreadWork2
mainThreadWork3
· 死锁:
下面是死锁的经典栗子:
为什么造成死锁呢?调度队列中的任务若要同步执行,后来的任务一定要等前面的任务执行完成后才能开始执行。下图是正常同一队列中任务同步执行的情况。
而在死锁栗子中,viewDidLoad()方法是在主线程执行的,也就是说它在Main Dispatch Queue中。而viewDidLoad()正在执行,还没执行完毕,说明它代表着任务1,在死锁栗子中添加了新的同步任务到Main Dispatch Queue。就是说任务1还没执行完,任务3就抢到最先要去马上执行,结果任务1跟任务3在关口那互相争抢造成死锁了。