在iOS中使用多線程的技術(shù)包括NSTread、NSOperationQueue、Grand Central Dispatch(GCD) 三種。NSThread繼承于NSObject類,創(chuàng)建方法略顯繁瑣,且是在ObjectIve-C中使用的。NSOperationQueue是對(duì)GCD的面向?qū)ο蟮姆庋b,在日常開發(fā)中,GCD是最為快捷簡便的,也是我日常使用最多的,而在Swift3.0中,基于 Swift語法封裝的DispatchQueue給Swift帶來了更為方便的使用,今天就來扒一扒這個(gè)DispatchQueue。
DispatchQueue-基本概念
一、串行和并發(fā)
DispatchQueue 即執(zhí)行任務(wù)的隊(duì)列,分為串行隊(duì)列(Serial Dispatch Queue)和并發(fā)隊(duì)列(Concurrent Dispatch Queue)。串行隊(duì)列中的任務(wù)是順序執(zhí)行的,即先放進(jìn)去的任務(wù)先執(zhí)行,后放入的人物后執(zhí)行。并發(fā)隊(duì)列的任務(wù)則可以在后臺(tái)中一起執(zhí)行。
二、同步和異步
同步(sync)和異步(async)的概念很容易和串行并發(fā)的概念混淆,這里特別說明一下:同步異步是線程之間的執(zhí)行方式,串行并發(fā)是單一隊(duì)列內(nèi)多個(gè)任務(wù)的執(zhí)行方式。
可以先看個(gè)案例:
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方法,也就是主線程中,創(chuàng)建任務(wù)隊(duì)列,先同步遍歷,再異步遍歷,然后再主線程遍歷,看下運(yùn)行結(jié)果:

下面再看下先異步后同步的情況:
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)
}
}

兩個(gè)實(shí)驗(yàn)結(jié)果可以得出結(jié)論:
1,同步執(zhí)行時(shí),只有該隊(duì)列內(nèi)任務(wù)完成時(shí),才會(huì)執(zhí)行下一個(gè)隊(duì)列;
2,異步執(zhí)行時(shí)并不影響下一個(gè)隊(duì)列的執(zhí)行。
那么為什么會(huì)出現(xiàn)這兩種不同的方式呢?也就是二者的使用情景有什么不同?
異步執(zhí)行因其不阻塞主線程,可以將任務(wù)周期長(如網(wǎng)絡(luò)請(qǐng)求)、CPU占用率高的操作放在后臺(tái)執(zhí)行。而當(dāng)不同線程需要操作相同變量時(shí),為了避免發(fā)生數(shù)據(jù)操作沖突和危險(xiǎn),同步線程就可以派上用場了。
3,任務(wù)項(xiàng)(WorkItem)
任務(wù)項(xiàng)就是代碼塊,即隊(duì)列里要執(zhí)行的任務(wù)代碼,一個(gè)隊(duì)列里可以有多個(gè)任務(wù)項(xiàng),任務(wù)項(xiàng)之間的執(zhí)行方式就是或串行、或并發(fā)。
4,優(yōu)先級(jí)和Quality of Service (Qos)
除了簡單的同步異步影響隊(duì)列的執(zhí)行,優(yōu)先級(jí)會(huì)直接決定隊(duì)列的執(zhí)行順序。queueWithQos()包含了隊(duì)列的重要程度和優(yōu)先級(jí)信息,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)
}
優(yōu)先級(jí)順序:userInteractive> userInitiated> default> utility> background> unspecified
以上概念理解之后,再使用DispatchQueue就很容易了。
DispatchQueue-API解析
-
便利構(gòu)造器
public convenience init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)
參數(shù)解釋:
label: 隊(duì)列名稱,最好是寫有意義名字,能一看就知道這個(gè)隊(duì)列要執(zhí)行什么任務(wù);
qos: 隊(duì)列重要性和優(yōu)先級(jí)(.userInteractive .userInitiated .default .utility .background .unspecified)
attributes: 可填寫兩個(gè)屬性:.concurrent表明隊(duì)列是并發(fā)隊(duì)列,.initiallyInactive表明隊(duì)列需要手動(dòng)開啟。不填寫時(shí)默認(rèn)隊(duì)列串行、自動(dòng)執(zhí)行。
autoreleaseFrequency: 官方文檔是說當(dāng)設(shè)為.workItem時(shí),所有異步任務(wù)提交的代碼塊會(huì)被封裝成獨(dú)立的任務(wù),在同步執(zhí)行的隊(duì)列則不受影響。
target : 目標(biāo)隊(duì)列
后兩個(gè)參數(shù)的意義我還不太理解,希望有人看到可以指教一下。
示例:
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()
注:手動(dòng)開始線程是為了在特定場景下執(zhí)行特定任務(wù),此處只是為了演示用法。
2.執(zhí)行方法
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)
這四個(gè)方法其實(shí)就是兩個(gè)方法,只不過一個(gè)是直接執(zhí)行block代碼,一個(gè)是執(zhí)行任務(wù)項(xiàng)。同樣的延遲執(zhí)行方法如下:
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執(zhí)行
delayQueue.asyncAfter(deadline: .now() + .seconds(2)) {
print(Date())
}
3.DispatchGroup
DispatchGroup就是把隊(duì)列放到一個(gè)組里統(tǒng)一管理,這些隊(duì)列執(zhí)行的任務(wù)一般是有關(guān)聯(lián)的,示例:
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是異步執(zhí)行的兩個(gè)隊(duì)列,queue1中令系統(tǒng)沉睡2s,方法group.wait()的作用是讓queue1執(zhí)行完畢后再執(zhí)行后續(xù)隊(duì)列(這里是queue2),否則queue2異步執(zhí)行。
group.notify()方法是在group內(nèi)的隊(duì)列都執(zhí)行完畢后才會(huì)執(zhí)行。
4,獲取主線程隊(duì)列
let mainQueue = DispatchQueue.main
mainQueue.async {
print("其實(shí)還是主線程")
}
5,全局隊(duì)列
DispatchQueue.global().async {
print("全局隊(duì)列")
}