0. 前言
GCD(Grand Central Dispatch)是 Apple 开发的一种多核编程的方法,主要的作用是优化应用程序对多核处理器的运用,是在线程池模式的基础上执行的并行任务。GCD中有两个重要的基本概念——任务 和 队列。
1. 任务和队列
任务
就是执行的操作,也就是在线程中执行的代码,封装在 Swift 的闭包(Closure)或者 OC 的块(block)里。根据能否具备开启新线程可以分成 同步执行 和 异步执行。
- 同步执行(sync) 只能在当前线程中执行任务,不能开新线程,当前的代码段没有执行完就不能够执行下一部分,会阻塞当前线程。
- 异步执行(async) 可以在新线程中执行任务,能够开启新线程,不用等待当前的代码段执行完就可以往下执行后续代码,不会阻塞当前线程。
队列
就是任务队列,是一种特殊的线性表,满足 FIFO 的原则。在 GCD 里分为 串行队列 和 并发队列。
- 串行队列(Serial Dispatch Queue) 一个任务完成后接着执行下一个任务。
- 并发队列(Concurrent Dispatch Queue) 自动开启多个线程进行多个任务并发执行,而且并发只有在异步执行(dispatch_async)情况下才有效。
2. 创建队列
系统默认队列——主队列(串行)
//主队列
let mainQueue = DispatchQueue.main
系统默认队列——全局队列(并发)
//全局队列
let globalQueue = DispatchQueue.global()
全局队列有 4 个执行的优先级,从高到低分别是:
.userInitiated,.default,.utility,.background
所以当需要创建指定某个优先级的全局队列时:
let defaultGlobalQueue = DispatchQueue.global(qos: .default)
自定义创建队列
//创建串行队列
let serialQueue = DispatchQueue(label: "serialQueue_1")
//创建优先级为default的串行队列
let serialQueue = DispatchQueue(label: "serialQueue_2", qos: .default)
//创建优先级为background的并发队列
let concurrentQueue = DispatchQueue(label: "concurrentQueue", qos: .background, attributes: .concurrent)
队列执行
队列的执行部分可以用 3 个任务来反映出任务执行的顺序。
同步并行
代码示例:
func syncConcurrent() {
let queue = DispatchQueue(label: "queue", attributes: .concurrent)
print("--- begin ---")
queue.sync {
for i in 1...3 {
print("--- 🅰️ \(i) ---\(Thread.current)")
}
}
queue.sync {
for i in 1...3 {
print("--- 🅱️ \(i) ---\(Thread.current)")
}
}
queue.sync {
for i in 1...3 {
print("--- 🅾️ \(i) ---\(Thread.current)")
}
}
print("--- end ---")
}
打印结果
--- begin ---
--- 🅰️ 1 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- 🅰️ 2 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- 🅰️ 3 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- 🅱️ 1 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- 🅱️ 2 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- 🅱️ 3 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- 🅾️ 1 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- 🅾️ 2 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- 🅾️ 3 ---<NSThread: 0x60400006f380>{number = 1, name = main}
--- end ---
任务是按照顺序逐个执行的,并且只有一个主线程在战斗
异步并行
代码示例
func asyncConcurrent() {
let queue = DispatchQueue(label: "queue", attributes: .concurrent)
print("--- begin ---")
queue.async {
for i in 1...3 {
print("--- 🅰️ \(i) ---\(Thread.current)")
}
}
queue.async {
for i in 1...3 {
print("--- 🅱️ \(i) ---\(Thread.current)")
}
}
queue.async {
for i in 1...3 {
print("--- 🅾️ \(i) ---\(Thread.current)")
}
}
print("--- end ---")
}
打印结果 1
--- begin ---
--- 🅰️ 1 ---<NSThread: 0x604000078bc0>{number = 3, name = (null)}
--- 🅱️ 1 ---<NSThread: 0x6080000747c0>{number = 4, name = (null)}
--- 🅰️ 2 ---<NSThread: 0x604000078bc0>{number = 3, name = (null)}
--- 🅾️ 1 ---<NSThread: 0x6040000793c0>{number = 5, name = (null)}
--- 🅱️ 2 ---<NSThread: 0x6080000747c0>{number = 4, name = (null)}
--- 🅰️ 3 ---<NSThread: 0x604000078bc0>{number = 3, name = (null)}
--- end ---
--- 🅾️ 2 ---<NSThread: 0x6040000793c0>{number = 5, name = (null)}
--- 🅱️ 3 ---<NSThread: 0x6080000747c0>{number = 4, name = (null)}
--- 🅾️ 3 ---<NSThread: 0x6040000793c0>{number = 5, name = (null)}
打印结果 2
--- begin ---
--- 🅰️ 1 ---<NSThread: 0x604000069140>{number = 3, name = (null)}
--- 🅱️ 1 ---<NSThread: 0x60800006aa40>{number = 4, name = (null)}
--- 🅰️ 2 ---<NSThread: 0x604000069140>{number = 3, name = (null)}
--- 🅱️ 2 ---<NSThread: 0x60800006aa40>{number = 4, name = (null)}
--- 🅾️ 1 ---<NSThread: 0x60800006a900>{number = 5, name = (null)}
--- 🅰️ 3 ---<NSThread: 0x604000069140>{number = 3, name = (null)}
--- end ---
--- 🅾️ 2 ---<NSThread: 0x60800006a900>{number = 5, name = (null)}
--- 🅱️ 3 ---<NSThread: 0x60800006aa40>{number = 4, name = (null)}
--- 🅾️ 3 ---<NSThread: 0x60800006a900>{number = 5, name = (null)}
每一次运行的执行顺序不一定相同,任务的执行是交替的,一共新开了 3 个线程在战斗。并且从 begin 和 end 的位置可以看出,3 个任务被添加到队列之后就立刻开始了战斗。
同步串行
func syncSerial() {
let queue = DispatchQueue(label: "queue")
print("--- begin ---")
queue.sync {
for i in 1...3 {
print("--- 🅰️ \(i) ---\(Thread.current)")
}
}
queue.sync {
for i in 1...3 {
print("--- 🅱️ \(i) ---\(Thread.current)")
}
}
queue.sync {
for i in 1...3 {
print("--- 🅾️ \(i) ---\(Thread.current)")
}
}
print("--- end ---")
}
打印结果
--- begin ---
--- 🅰️ 1 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- 🅰️ 2 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- 🅰️ 3 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- 🅱️ 1 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- 🅱️ 2 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- 🅱️ 3 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- 🅾️ 1 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- 🅾️ 2 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- 🅾️ 3 ---<NSThread: 0x6000000777c0>{number = 1, name = main}
--- end ---
打印结果和同步并行一个样,都是只有主线程在战斗,并且按照顺序逐一执行。
串行异步
代码示例
func asyncSerial() {
let queue = DispatchQueue(label: "queue")
print("--- begin ---")
queue.async {
for i in 1...3 {
print("--- 🅰️ \(i) ---\(Thread.current)")
}
}
queue.async {
for i in 1...3 {
print("--- 🅱️ \(i) ---\(Thread.current)")
}
}
queue.async {
for i in 1...3 {
print("--- 🅾️ \(i) ---\(Thread.current)")
}
}
print("--- end ---")
}
打印结果
--- begin ---
--- 🅰️ 1 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
--- 🅰️ 2 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
--- 🅰️ 3 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
--- end ---
--- 🅱️ 1 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
--- 🅱️ 2 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
--- 🅱️ 3 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
--- 🅾️ 1 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
--- 🅾️ 2 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
--- 🅾️ 3 ---<NSThread: 0x60400007a700>{number = 3, name = (null)}
任务是按顺序逐一进行的,开启了一条新的线程。
主队列和全局队列的同步/异步
同步
DispatchQueue.global(qos: .default).async {
print("I'm in global.")
DispatchQueue.main.async {
print("I'm back in main.")
}
}
异步
//Global Dispatch Queue
//不会死锁,但代码顺序执行,后续代码需要等待前面代码执行完毕
DispatchQueue.global(qos: .default).sync {
print("I'm in global.")
print("I'm still in global.")
}
print("I'm out.")
//Main Queue
//死锁。按照同步的执行顺序,一个任务需要等待前一个任务执行完毕,但是"I'm in main."是添加在"I'm out."之后,但是"I'm out"又需要等待"I'm in main."执行完毕之后再执行。所以彼此等待造成了死锁。
DispatchQueue.main.sync {
print("I'm in main.")
}
print("I'm out.")
队列暂停和继续
//暂停
queue.suspend()
//继续
queue.resume()
suspend() 和 resume() 是异步函数,在两个闭包之间生效。
suspend() 会使得已经添加到 Dispatch Queue 但是还没有执行的任务在改行代码之后暂停执行,等到 resume() 之后才能继续执行。
单次执行
单次执行在多线程编程的使用过程中很常见,当然除了多线程之外也有一些其他的用途。
在 Swift 3 之前,单次执行可以通过 dispatch_once 来实现,但是 Swift 3 把它废弃了... 查了一些资料发现要实现原来的 dispatch_once 可以通过下面几个方式:
- 全局常量
let constant = SomeClass()
- 全局变量(带有立即执行的闭包构造器)
var variable: SomeClass = {
let constant = SomeClass()
constant.oneProperty = "HeySiri"
constant.oneMethod()
return constant
}()
网友评论