GCD
Thread -> GCD -> Operation + OperationQueue(life cycle, dependencies between different queues, cancel)
[同步 vs 异步]
[iOS 线程安全]
Grand Central Dispatch(GCD)
libdispatch
在调度队列DispatchQueue
上操作。它按照先进先出(FIFO)顺序工作。
DispatchQueue.<queue>.<sync/async>
表示在<queue>
上运行一个<sync/async>
任务
队列可以是串行的或并发的。并发(更加并行)允许同时处理多个任务,而串行则一个接一个地处理。并发任务按照添加的顺序启动,但可以以不同的顺序完成。
GCD
支持:
主队列
- 应用程序主线程上的串行队列,用于处理UI
全局队列
- 在整个iOS操作系统之间共享的并发队列
私有队列
- 应用程序范围内的串行/并发队列
主队列
//Thread.current.qualityOfService = .userInitiated
DispatchQueue.main
全球队列
DispatchQueue.global(qos: DispatchQoS.QoSClass = .default)
私人队列
DispatchQueue(label: String, qos: DispatchQoS = .unspecified, attributes: DispatchQueue.Attributes = [], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit, target: DispatchQueue? = nil)
//Serial Queue
DispatchQueue(label: "first.myApp.com")
//Concurrent Queue - attributes: .concurrent
DispatchQueue(label: "second.myApp.com", attributes: .concurrent)
服务质量(QoS)
每个队列都有一个qos,它决定了其优先级。
userInteractive
- 最高优先级 - 速度优先于能耗。任务的结果会反映在用户界面上。任务应该小而快速产生结果。例如动画。
userInitiated
- 用户启动的任务,其结果是异步的。例如加载数据/打开本地文件以在用户界面上显示结果。
utility
- 长时间运行的计算,最多几分钟,例如保存数据到文件...
background
- 最低优先级 - 能耗优先于速度。非常长时间运行的计算,几分钟以上,例如同步数据。
应避免使用的其他QoS:
- default - 优先级介于.userInitiated和.utility之间。
- unspecified - 使用外部线程的QoS(继承调用者的QoS)。例如当您从主线程调用时,
thread.qualityOfService == .userInitiated
。
func examineQoS(qos: DispatchQoS) {
let queue = <create_queue>
queue.async {
print("""
QoS
qos: \(qos.qosClass)
queue.qos: \(queue.qos.qosClass)
thread.qualityOfService: \(Thread.current.qualityOfService)
""")
}
}
let queue = DispatchQueue.global()
let queue = DispatchQueue(label: "mySerial")
let queue = DispatchQueue(label: "myConcurrent", attributes: .concurrent)
let queue = DispatchQueue.global(qos: qos.qosClass)
let queue = DispatchQueue(label: "mySerial", qos: qos)
let queue = DispatchQueue(label: "myConcurrent", qos: qos, attributes: .concurrent)
- 工作线程的数量取决于操作系统的条件。工作线程没有运行循环。
同步/异步
同步
- 阻塞当前线程,并等待在指定队列上完成
异步
- 不阻塞当前线程,并将代码执行块发送到指定队列
//deadline in seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// logic
}
常见错误:死锁
如果您在main
线程上调用DispatchQueue.main.sync
- 应用程序将被冻结,因为调用DispatchQueue.main.sync
时,调度的块完成后立即开始等待(调度的块尚未启动)
一些注意事项:
DispatchWorkItem
- 在Queue
或DispatchGroup
中延迟/取消/优先执行任务
DispatchGroup
,如果您要在不同的队列上执行多个异步任务并使用单个回调。所有这些任务应该被分组。 DispatchGroup
包含线程安全计数器,当它等于0时,调用notify
//create group
let group = DispatchGroup()
//case 1
DispatchQueue.<queue>.async(group: group) //
//case 2 - manual
group.enter() //<- +1
DispatchQueue.global().async {
//logic
group.leave() //<- -1
}
//notification
group.notify(queue: <callback_queue>) {
//logic
}
Barrier
标志位在并发队列中用于同步/异步任务,确保没有竞态条件
[关于]。最好的位置是自定义队列,因为它不会阻塞其他全局任务:
customQueue.async(flags: .barrier) {
someProperty = someValue
}
所有已开始的任务都已完成
单一屏障任务
执行队列中的所有其他任务
通过并发队列中的屏障可以实现线程安全操作,用于共享变量:
读取 - 并发队列上的同步操作
写入 - 使用屏障的异步操作
[线程安全的单例模式]
async
意味着它不会阻塞,但你正在调用DispatchQueue.main
,这是一个保证在主线程上运行的队列。" - Ssswift