在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("全局隊列")
}