在iOS中使用多线程的技术包括NSTread、NSOperationQueue、Grand Central Dispatch(GCD) 三种。NSThread
继承于NSObject
类,创建方法略显繁琐,且是在ObjectIve-C中使用的。NSOperationQueue
是对GCD
的面向对象的封装,在日常开发中,GCD是最为快捷简便的,也是我日常使用最多的,而在Swift3.0中,基于 Swift语法封装的DispatchQueue
给Swift带来了更为方便的使用,今天就来扒一扒这个DispatchQueue
。
DispatchQueue-基本概念
一、串行和并发
DispatchQueue
即执行任务的队列,分为串行队列(Serial Dispatch Queue)和并发队列(Concurrent Dispatch Queue)。串行队列中的任务是顺序执行的,即先放进去的任务先执行,后放入的人物后执行。并发队列的任务则可以在后台中一起执行。
二、同步和异步
同步(sync)和异步(async)的概念很容易和串行并发的概念混淆,这里特别说明一下:同步异步是线程之间的执行方式,串行并发是单一队列内多个任务的执行方式。
可以先看个案例:
override func viewDidLoad() {
super.viewDidLoad()
let queue1: DispatchQueue = DispatchQueue(label: "com.artron.myqueue")
let queue2: DispatchQueue = DispatchQueue(label: "com.artron.myqueue")
queue1.sync {
for i in 0..<10 {
print("🌧", i)
}
}
queue2.async {
for i in 10..<20 {
print("🔥", i)
}
}
for i in 100..<110 {
print("💧", i)
}
}
在viewDidLoad
方法,也就是主线程中,创建任务队列,先同步遍历,再异步遍历,然后再主线程遍历,看下运行结果:

下面再看下先异步后同步的情况:
override func viewDidLoad() {
super.viewDidLoad()
let queue1: DispatchQueue = DispatchQueue(label: "com.artron.myqueue")
let queue2: DispatchQueue = DispatchQueue(label: "com.artron.myqueue")
queue1.async {
for i in 10..<20 {
print("🔥", i)
}
}
queue2.sync {
for i in 0..<10 {
print("🌧", i)
}
}
for i in 100..<110 {
print("💧", i)
}
}

两个实验结果可以得出结论:
1,同步执行时,只有该队列内任务完成时,才会执行下一个队列;
2,异步执行时并不影响下一个队列的执行。
那么为什么会出现这两种不同的方式呢?也就是二者的使用情景有什么不同?
异步执行因其不阻塞主线程,可以将任务周期长(如网络请求)、CPU占用率高的操作放在后台执行。而当不同线程需要操作相同变量时,为了避免发生数据操作冲突和危险,同步线程就可以派上用场了。
3,任务项(WorkItem)
任务项就是代码块,即队列里要执行的任务代码,一个队列里可以有多个任务项,任务项之间的执行方式就是或串行、或并发。
4,优先级和Quality of Service (Qos)
除了简单的同步异步影响队列的执行,优先级会直接决定队列的执行顺序。queueWithQos()包含了队列的重要程度和优先级信息,Swift中用枚举描述:
/// qos_class_t
public struct DispatchQoS : Equatable {
public let qosClass: DispatchQoS.QoSClass
public let relativePriority: Int
public static let background: DispatchQoS
public static let utility: DispatchQoS
public static let `default`: DispatchQoS
public static let userInitiated: DispatchQoS
public static let userInteractive: DispatchQoS
public static let unspecified: DispatchQoS
public enum QoSClass {
case background
case utility
case `default`
case userInitiated
case userInteractive
case unspecified
public init?(rawValue: qos_class_t)
public var rawValue: qos_class_t { get }
}
public init(qosClass: DispatchQoS.QoSClass, relativePriority: Int)
}
优先级顺序:userInteractive> userInitiated> default> utility> background> unspecified
以上概念理解之后,再使用DispatchQueue
就很容易了。
DispatchQueue-API解析
-
便利构造器
public convenience init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)
参数解释:
label: 队列名称,最好是写有意义名字,能一看就知道这个队列要执行什么任务;
qos: 队列重要性和优先级(.userInteractive .userInitiated .default .utility .background .unspecified)
attributes: 可填写两个属性:.concurrent
表明队列是并发队列,.initiallyInactive
表明队列需要手动开启。不填写时默认队列串行、自动执行。
autoreleaseFrequency: 官方文档是说当设为.workItem
时,所有异步任务提交的代码块会被封装成独立的任务,在同步执行的队列则不受影响。
target : 目标队列
后两个参数的意义我还不太理解,希望有人看到可以指教一下。
示例:
let autoQueue: DispatchQueue = DispatchQueue(label: "defaultQueue", qos: .userInitiated)
autoQueue.async {
print("I will auto activate")
}
let initiallyInactiveQueue : DispatchQueue = DispatchQueue(label: "initiallyInactiveQueue", qos: DispatchQoS.userInitiated, attributes: [.concurrent, .initiallyInactive])
initiallyInactiveQueue.async {
print("I need be activate manual")
}
initiallyInactiveQueue.activate()
注:手动开始线程是为了在特定场景下执行特定任务,此处只是为了演示用法。
2.执行方法
public func sync(execute workItem: DispatchWorkItem)
,
public func async(execute workItem: DispatchWorkItem)
,
public func sync(execute block: () -> Swift.Void)
,
public func async(execute block: () -> Swift.Void)
这四个方法其实就是两个方法,只不过一个是直接执行block代码,一个是执行任务项。同样的延迟执行方法如下:
public func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem)
,
public func asyncAfter(deadline: DispatchTime, qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, execute work: @escaping @convention(block) () -> Swift.Void)
let delayQueue = DispatchQueue(label: "delayQueue");
// 延迟2s执行
delayQueue.asyncAfter(deadline: .now() + .seconds(2)) {
print(Date())
}
3.DispatchGroup
DispatchGroup
就是把队列放到一个组里统一管理,这些队列执行的任务一般是有关联的,示例:
let group = DispatchGroup()
let queue1 = DispatchQueue(label: "work1")
queue1.async(group: group) {
print("干完work1")
sleep(2)
}
group.wait()
let queue2 = DispatchQueue(label: "work2")
queue2.async(group: group){
print("干完work2")
}
group.notify(queue: DispatchQueue.main) {
print("都干完了,可以下班了")
}
分析:queue1
和queue2
是异步执行的两个队列,queue1
中令系统沉睡2s,方法group.wait()
的作用是让queue1
执行完毕后再执行后续队列(这里是queue2
),否则queue2
异步执行。
group.notify()
方法是在group内的队列都执行完毕后才会执行。
4,获取主线程队列
let mainQueue = DispatchQueue.main
mainQueue.async {
print("其实还是主线程")
}
5,全局队列
DispatchQueue.global().async {
print("全局队列")
}
网友评论