基本概念
- 并行: 多个任务同时执行
- 串行:一个任务执行完成后,再执行下一个任务
- 同步:在当前线程中执行任务,不会开启新线程
- 异步:在新的线程中执行任务
GCD的Dispatch Queues
Grand Central Dispatch 的基本概念就是dispatch queue调度队列,可以是并发的,可以是串行的。调度队列有三种类型:
- The main queue 串行队列,和应用程序的主线程功能相同。通过DispatchQueue.main来获得main队列
- Global queues 全局队列是并行队列,全局队列有高、中、低、后台4个优先级,默认优先级是中。通过DispatchQueue.global来获得全局队列
- 用户线程队列 通过DispatchQueue.init来获得用户线程队列,以串行/并行的队列。
DispatchGroup
DispatchGroup对多个任务进行管理和调度。enter与leave相对应。
DispatchGroup-wait
当前线程等待,直至DispatchGroup任务调度结束,堵塞当前线程(主线程)。
let workdingGroup = DispatchGroup()
let workdingQueue = DispatchQueue(label: "queue")//默认为串行队列
workdingGroup.enter()
workdingQueue.async {
Thread.sleep(forTimeInterval: 1)
print("第一个异步操作")
workdingGroup.leave()
}
workdingGroup.enter()
workdingQueue.async {
Thread.sleep(forTimeInterval: 1)
print("第二个异步操作")
workdingGroup.leave()
}
print("主线程操作")
workdingGroup.wait()
print("DispatchGroup调度堵塞结束")
//输出
主线程操作
第一个异步操作
第二个异步操作
DispatchGroup调度堵塞结束
DispatchGroup-notify
监听DispatchGroup任务完成状态,完成后触发闭包回调,不堵塞当前线程(主线程)。
let workdingGroup = DispatchGroup()
let workdingQueue = DispatchQueue(label: "queue")//默认为串行队列
workdingGroup.enter()
workdingQueue.async {
Thread.sleep(forTimeInterval: 1)
print("第一个异步操作")
workdingGroup.leave()
}
workdingGroup.enter()
workdingQueue.async {
Thread.sleep(forTimeInterval: 1)
print("第二个异步操作")
workdingGroup.leave()
}
print("主线程操作")
workdingGroup.notify(queue: workdingQueue) {
print("两个异步任务完成")
}
print("DispatchGroup调度不阻塞主线程")
//输出
主线程操作
DispatchGroup调度不阻塞主线程
第一个异步操作
第二个异步操作
两个异步任务完成
实例:实现线程安全的Array读与写
var array = Array(0...10000)
let queue1 = DispatchQueue(label: "quque1", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
let queue2 = DispatchQueue(label: "quque2", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
func getLastItem() -> Int? {
let count = array.count
if count > 0 {
return array[count - 1]
}
return nil
}
func removeLastItem() {
array.removeLast()
}
queue1.async {
for _ in 0...10000 {
if let lastNum = getLastItem() {
print(lastNum)
}
}
}
queue2.async {
for _ in 0...10000 {
removeLastItem()
}
}
//输出
10000
9856
9850
9845
9841
9837
9834
9830
9830
9830
9830
9830
9830
9830
9830
9830
9817
9812
9809
Fatal error: Index out of range
由于读与写操作是异步执行的,会存在要访问的下标之前已经删除元素而出现下标越界的情况。所以需要对两个并行异步线程进行安全管理。
第一种方案 线程加锁
var array = Array(0...10000)
let queue1 = DispatchQueue(label: "quque1", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
let queue2 = DispatchQueue(label: "quque2", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
let lock = NSLock()
func getLastItem() -> Int? {
lock.lock()
let count = array.count
if count > 0 {
return array[count - 1]
}
lock.unlock()
return nil
}
func removeLastItem() {
lock.lock()
array.removeLast()
lock.unlock()
}
queue1.async {
for _ in 0...10000 {
if let lastNum = getLastItem() {
print(lastNum)
}
}
}
queue2.async {
for _ in 0...10000 {
removeLastItem()
}
}
虽然不会出现数组访问越界的情况,但是如果对读操作多次使用而每次使用都要进行上锁和解锁操作,无疑会对性能产生影响。
第二种方法:barrier async
等待barrier async 之前的函数完成,再完成barrier async ,再完成barrier async 之后的函数。
var array = Array(0...10000)
let queue1 = DispatchQueue(label: "quque1", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
let queue2 = DispatchQueue(label: "quque2", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
let arrayQueue = DispatchQueue(label: "quque3", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
func getLastItem() -> Int? {
return arrayQueue.sync { () -> Int? in
if array.count > 0 {
return array[array.count - 1]
}
return nil
}
}
func removeLastItem() {
let workItem = DispatchWorkItem(qos: DispatchQoS.default, flags: DispatchWorkItemFlags.barrier) {
array.removeLast()
}
arrayQueue.async(execute: workItem)
}
queue1.async {
for _ in 0...10000 {
if let item = getLastItem() {
print(item)
}
}
}
queue2.async {
for _ in 0...10000 {
removeLastItem()
}
}