-GCD
中的核心詞是dispatch queue
。一個(gè)隊(duì)列實(shí)際上就是一系列的代碼塊,這些代碼可以在主線程或后臺(tái)線程中以同步或者異步的方式執(zhí)行。一旦隊(duì)列創(chuàng)建完成,操作系統(tǒng)就接管了這個(gè)隊(duì)列,并將其分配到任意一個(gè)核心中進(jìn)行處理。不管有多少個(gè)隊(duì)列,它們都能被系統(tǒng)正確地管理,這些都不需要開(kāi)發(fā)者進(jìn)行手動(dòng)管理。隊(duì)列遵循 FIFO 模式(先進(jìn)先出),這意味著先進(jìn)隊(duì)列的任務(wù)會(huì)先被執(zhí)行(想像在柜臺(tái)前排隊(duì)的隊(duì)伍,排在第一個(gè)的會(huì)首先被服務(wù),排在最后的就會(huì)最后被服務(wù))。
- 另一個(gè)重要的概念就是
WorkItem
(任務(wù)項(xiàng))。一個(gè)任務(wù)項(xiàng)就是一個(gè)代碼塊,它可以隨同隊(duì)列的創(chuàng)建一起被創(chuàng)建,也可以被封裝起來(lái),然后在之后的代碼中進(jìn)行復(fù)用。正如你所想,任務(wù)項(xiàng)的代碼就是dispatch queue
將會(huì)執(zhí)行的代碼。隊(duì)列中的任務(wù)項(xiàng)也是遵循 FIFO 模式。這些執(zhí)行可以是同步的,也可以是異步的。對(duì)于同步的情況下,應(yīng)用會(huì)一直堵塞當(dāng)前線程,直到這段代碼執(zhí)行完成。而當(dāng)異步執(zhí)行的時(shí)候,應(yīng)用先執(zhí)行任務(wù)項(xiàng),不等待執(zhí)行結(jié)束,立即返回。 - 在為主隊(duì)列添加任務(wù)時(shí),無(wú)論何時(shí)都要加倍小心。這個(gè)隊(duì)列要隨時(shí)用于界面響應(yīng)以及用戶交互。并且記住一點(diǎn),所有與用戶界面相關(guān)的更新都必須在主線程執(zhí)行。如果你嘗試在后臺(tái)線程更新 UI,系統(tǒng)并不保證這個(gè)更新何時(shí)會(huì)發(fā)生,大多數(shù)情況下,這會(huì)都用戶帶來(lái)不好的體驗(yàn)。但是,所有發(fā)生在界面更新前的任務(wù)都可以在后臺(tái)線程執(zhí)行。
- 我們不一定需要每次都創(chuàng)建自己的隊(duì)列。系統(tǒng)維護(hù)的全局隊(duì)列可以用來(lái)執(zhí)行任何我們想執(zhí)行的任務(wù)。至于隊(duì)列在哪一個(gè)線程運(yùn)行,iOS 維護(hù)了一個(gè)線程池,即一系列除主線程之外的線程,系統(tǒng)會(huì)從中挑選一至多條線程來(lái)使用(取決于你所創(chuàng)建的隊(duì)列的數(shù)據(jù),以及隊(duì)列創(chuàng)建的方式)。哪一條線程會(huì)被使用,對(duì)于開(kāi)發(fā)者來(lái)說(shuō)是未知的,而是由系統(tǒng)根據(jù)當(dāng)前的并發(fā)任務(wù),處理器的負(fù)載等情況來(lái)進(jìn)行“決定”。
Dispatch Queue
在 swift 中 dispatch queue
對(duì)應(yīng)的類為DispatchQueue
,可以使用下面方法進(jìn)行初始化
let queue = (label: String,
qos: DispatchQoS = default,
attributes: DispatchQueue.Attributes = default,
autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default,
target: DispatchQueue? = default)
在 Swift 3 當(dāng)中,創(chuàng)建一個(gè)dispatch queue
的最簡(jiǎn)單方式:
let queue = DispatchQueue(label: "com.appcode.myqueue")
queue.sync {
print("同步執(zhí)行")
}
queue.async {
print("異步執(zhí)行")
}
Quality Of Service (QOS) 和優(yōu)先級(jí)
在使用
GCD
與dispatch queue
時(shí),我們經(jīng)常需要告訴系統(tǒng),應(yīng)用程序中的哪些任務(wù)比較重要,需要更高的優(yōu)先級(jí)去執(zhí)行。當(dāng)然,由于主隊(duì)列總是用來(lái)處理 UI 以及界面的響應(yīng),所以在主線程執(zhí)行的任務(wù)永遠(yuǎn)都有最高的優(yōu)先級(jí)。不管在哪種情況下,只要告訴系統(tǒng)必要的信息,iOS 就會(huì)根據(jù)你的需求安排好隊(duì)列的優(yōu)先級(jí)以及它們所需要的資源(比如說(shuō)所需的 CPU 執(zhí)行時(shí)間)。雖然所有的任務(wù)最終都會(huì)完成,但是,重要的區(qū)別在于哪些任務(wù)更快完成,哪些任務(wù)完成得更晚。用于指定任務(wù)重要程度以及優(yōu)先級(jí)的信息,在 GCD 中被稱為
Quality of Service (Qos)
。事實(shí)上,Qos 是有幾個(gè)特定值的枚舉類型,我們可以根據(jù)需要的優(yōu)先級(jí),使用合適的 Qos 值來(lái)初始化隊(duì)列。如果沒(méi)有指定 Qos,則隊(duì)列會(huì)使用默認(rèn)優(yōu)先級(jí)進(jìn)行初始化。
/// 由低到高 優(yōu)先級(jí)
public static let unspecified: DispatchQoS
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
并發(fā)隊(duì)列
///public static let concurrent: DispatchQueue.Attributes
///public static let initiallyInactive: DispatchQueue.Attributes
///attributes 參數(shù)也可以接受另一個(gè)名為 initiallyInactive 的值。如果使用這個(gè)值,
///任務(wù)不會(huì)被自動(dòng)執(zhí)行,而是需要開(kāi)發(fā)者手動(dòng)去觸發(fā)。
let anotherQueue = DispatchQueue(label:"com.appcode.anotherQueue",
qos: .utility, attributes: .concurrent)
-
DispatchQueue
類的activate()
方法會(huì)讓任務(wù)開(kāi)始執(zhí)行。注意,這個(gè)隊(duì)列并沒(méi)有被指定為并發(fā)隊(duì)列,因此它們會(huì)以串行的方式執(zhí)行。
let anotherQueue = DispatchQueue(label:"com.appcode.anotherQueue",
qos: .utility, attributes: .initiallyInactive)
anotherQueue.activate()
- 現(xiàn)在的問(wèn)題是,我們?nèi)绾卧谥付?code>initiallyInactive 的同時(shí)將隊(duì)列指定為并發(fā)隊(duì)列?其實(shí)很簡(jiǎn)單,我們可以將兩個(gè)值放入一個(gè)數(shù)組當(dāng)中,作為
attributes
的參數(shù),替代原本指定的單一數(shù)值。
let anotherQueue = DispatchQueue(label:"com.appcode.anotherQueue",
qos: .userInitiated, attributes: [.concurrent, .initiallyInactive])
延遲執(zhí)行
let delayQueue = DispatchQueue(label: "com.appcoda.delayqueue", qos: .userInitiated)
print(Date())
let additionalTime: DispatchTimeInterval = .seconds(2)
delayQueue.asyncAfter(deadline: .now() + additionalTime) {
print(Date())
}
全局隊(duì)列和主隊(duì)列
let globalQueue = DispatchQueue.global(qos: .userInitiated)
DispatchQueue.main.async {
// Do something
}
DispatchWorkItem
-DispatchWorkItem
是一個(gè)代碼塊,它可以在任意一個(gè)隊(duì)列上被調(diào)用,因此它里面的代碼可以在后臺(tái)運(yùn)行,也可以在主線程運(yùn)行。它的使用真的很簡(jiǎn)單,就是一堆可以直接調(diào)用的代碼,而不用像之前一樣每次都寫(xiě)一個(gè)代碼塊。
var value = 10
let workItem = DispatchWorkItem {
value += 5
}
workItem.perform()/// 這行代碼會(huì)在主線程上面調(diào)用
///其他隊(duì)列執(zhí)行
let queue = DispatchQueue.global()
queue.async {
workItem.perform()
}
queue.async(execute: workItem)
- 當(dāng)一個(gè)任務(wù)項(xiàng)被調(diào)用后,你可以通知主隊(duì)列(或者任何其它你想要的隊(duì)列)
workItem.notify(queue: DispatchQueue.main) {
print("value = ", value)
}
var value = 10
let workItem = DispatchWorkItem {
value += 5
}
workItem.perform()
let queue = DispatchQueue.global(qos: .utility)
queue.async(execute: workItem)
workItem.notify(queue: DispatchQueue.main) {
print("value = ", value)
}