一、概念
队列(DispatchQueue)
Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. Work submitted to dispatch queues executes on a pool of threads managed by the system. Except for the dispatch queue representing your app's main thread, the system makes no guarantees about which thread it uses to execute a task.
调度队列是一个FIFO(先进先出)队列,应用程序可以以block对象的形式向其提交任务。dispatch队列可以串行或并发执行任务。提交到调度队列的任务执行在系统管理的线程池上。除了代表应用程序主线程的调度队列外,系统不保证是哪条线程执行任务
由上面看出队列有串行、并行两种,那么他们有何区别呢?
串行队列(Serial Dispatch Queue)
每次只执行一个任务。任务是按照进入队列的顺序执行的,并且只会开启一条线程
并发队列(Concurrent Dispatch Queue)
多个任务并发执行,且可以开启多条线程
任务
任务就是我们在线程中执行的那段代码,分为同步任务(sync)和异步任务(async)。其主要区别是:是否等待任务执行完,以及是否具备开启新线程的能力
同步任务(sync)
同步任务添加到指定的队列中,在它未执行完成之前,会一直等待,直到任务完成后才会返回;只能在当前线程中执行任务,不具备开启新线程的能力
异步任务(async)
异步任务添加到指定的队列中,它不会等待任务完成,就会返回。可以在新的线程中执行任务,具备开启新线程的能力
1.串行队列同步任务
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 创建串行队列
let queue = DispatchQueue(label: "myQueue", qos: .default)
queue.sync {
Thread.sleep(forTimeInterval: 1)
print("run task1 thread \(Thread.current)")
}
queue.sync {
Thread.sleep(forTimeInterval: 0.5)
print("run task2 thread \(Thread.current)")
}
}
输出:
run task1 thread <NSThread: 0x6000012059c0>{number = 1, name = main}
run task2 thread <NSThread: 0x6000012059c0>{number = 1, name = main}
2.串行队列异步任务
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 创建串行队列
let queue = DispatchQueue(label: "myQueue", qos: .default)
queue.async {
Thread.sleep(forTimeInterval: 1)
print("run task1 thread \(Thread.current)")
}
queue.async {
Thread.sleep(forTimeInterval: 0.5)
print("run task2 thread \(Thread.current)")
}
}
输出:
run task1 thread <NSThread: 0x600001b33dc0>{number = 3, name = (null)}
run task2 thread <NSThread: 0x600001b33dc0>{number = 3, name = (null)}
3.并发队列同步任务
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 全局并行队列
let queue = DispatchQueue.global()
queue.sync {
Thread.sleep(forTimeInterval: 1)
print("run task1 thread \(Thread.current)")
}
queue.sync {
Thread.sleep(forTimeInterval: 0.5)
print("run task2 thread \(Thread.current)")
}
queue.sync {
print("run task3 thread \(Thread.current)")
}
}
输出:
run task1 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}
run task2 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}
run task3 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}
4.并发队列异步任务
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue.global()
queue.async {
print("run task1 thread \(Thread.current)")
}
queue.async {
print("run task2 thread \(Thread.current)")
}
queue.async {
print("run task3 thread \(Thread.current)")
}
}
输出:
run task3 thread <NSThread: 0x600003589100>{number = 3, name = (null)}
run task2 thread <NSThread: 0x600003589140>{number = 4, name = (null)}
run task1 thread <NSThread: 0x6000035b76c0>{number = 5, name = (null)}
GCD死锁
GCD死锁就是,两个任务相互等待,从而造成死锁
死锁原因?两个任务相互等待
例:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("start")
DispatchQueue.main.sync { // 死锁
print("ahaha")
}
print("end")
}
输出:
start
怎么理解?
我们先看下面的代码
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("Hello, World!")
run()
print("game over")
}
func run() {
print("do func run")
}
输出:
Hello, World!
do func run
game over
我们在学校的时候老师告诉我们代码是按顺序执行,所以很好理解上面的输出,即执行完打印hello world,就执行run函数,当执行完run后再打印game over。再次印证了代码是一步一步执行的
,很好请记住这句,很重要。
回到死锁的例子来分析原因。首先执行start,这毫无疑问;然后执行同步任务,同步任务的特点是执行完同步任务,再返回,意思是要等这个任务执行完才会执行end;然后我们来看下这个任务是加在哪个队列?一看是DispatchQueue.main即主队列,而当前主队列是在执行viewDidLoad,也就是说这个同步任务是加在viewDidLoad之后。因为主队列是一个串行队列,也就是说,队列里的任务是一个接着一个执行,只有前一个任务执行完,才会执行下一个。那么问题就出现了代码执行到DispatchQueue.main.sync这里时,主队列阻塞了,要等到任务执行完,但是主队列的任务即viewDidLoad需要等到该任务执行完才会执行下一个任务,这就造成了两个任务互相等待,从而死锁
而下面这个例子将再次印证代码是一步一步执行的
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("start")
DispatchQueue.global().sync { // 不死锁
print("ahaha")
}
print("end")
}
输出:
start
ahaha
end
这为什么没有死锁呢?因为这个同步任务是加在全局并发队列里,不是加在主队列,因此当全局并发队列执行完这个任务,就会执行end
二、GCD相关函数
2.1GCD栅栏方法
执行队列中在barrier之前加入的任务,才会执行在barrier之后加入的任务。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue(label: "hahah", qos: .default, attributes: .concurrent)
queue.async {
print("begin task1..... will sleep 2 second")
Thread.sleep(forTimeInterval: 2)
print("end task1")
}
queue.async {
print("begin task2.....will sleep 3 second")
Thread.sleep(forTimeInterval: 3)
print("end task2")
}
let item = DispatchWorkItem(qos: .default, flags: .barrier) {
print("begin barrier sleep 2 second")
Thread.sleep(forTimeInterval: 2)
print("end barrier")
}
queue.async(execute: item)
queue.async {
print("task3....")
}
}
//输出:
begin task1..... will sleep 2 second
begin task2.....will sleep 3 second
end task1
end task2
begin barrier sleep 2 second
end barrier
task3....
2.2 group notify
group.notify当group中的任务执行完之后才会执行并且不会阻塞队列
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue.global()
let group = DispatchGroup()
let download = DispatchWorkItem {
print("task1...")
}
queue.async(group: group, execute: download)
let saveDb = DispatchWorkItem {
print("task2...")
}
queue.async(group: group, execute: saveDb)
group.notify(queue: DispatchQueue.main) {
print("all finish")
}
print("end")
}
//输出:
task2...
end
task1...
all finish
如果task是异步任务,那么notify的任务就不会等到task1、task2完成,那要怎么做了,使用enter、level,注意enter、level需要成对出现
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue.global()
let group = DispatchGroup()
group.enter()
let download = DispatchWorkItem {
queue.async {
Thread.sleep(forTimeInterval: 2)
print("download task1 fininsh")
group.leave()
}
}
queue.async(group: group, execute: download)
group.enter()
let saveDb = DispatchWorkItem {
queue.async {
Thread.sleep(forTimeInterval: 1.5)
print("download task2 finish")
group.leave()
}
}
queue.async(group: group, execute: saveDb)
group.notify(queue: DispatchQueue.main) {
print("all finish")
}
}
// 输出
download task2 finish
download task1 fininsh
all finish
2.3信号量
使用DispatchSemaphore创建信号量
调用single()会使信号量+1
调用wait()会使信号量-1,当信号量小于0时阻塞当前线程
因此可以使用信号量可以让多个异步任务达到同步执行的效果
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global().async {
print("run task1")
Thread.sleep(forTimeInterval: 2)
semaphore.signal()
}
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
DispatchQueue.global().async {
print("run task2")
Thread.sleep(forTimeInterval: 1)
semaphore.signal()
}
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
DispatchQueue.global().async {
print("run task3")
}
}
//输出:
run task1
run task2
run task3
2.4延迟执行
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue.global()
let time = DispatchTime.now() + 3
queue.asyncAfter(deadline: time) {
print("after 3 sencond")
}
}
上面的3也可以使用DispatchTimeInterval的second、milliseconds、microseconds、nanoseconds来代替
网友评论