從Swift3開始GCD的API就發(fā)生了很大的變化详拙,更加簡(jiǎn)潔嘹朗,使用起來(lái)更方便。像我們經(jīng)常開啟一個(gè)異步線程處理事情然后切回主線程刷新UI操作诗祸,這里就變的非常簡(jiǎn)單了跑芳。
? ? DispatchQueue.global().async {
? ? ? ? // do async task
? ? ? ? DispatchQueue.main.async {
? ? ? ? ? ? // update UI
? ? ? ? ? }
? ? ? }
DispatchQueue
DispatchQueue字面意思就是派發(fā)列隊(duì),主要是管理需要執(zhí)行的任務(wù)直颅,任務(wù)以閉包或者DispatchWorkItem的方式進(jìn)行提交.列隊(duì)中的任務(wù)遵守FIFO原則博个。如果對(duì)于列隊(duì)不是很了解,可以看這里际乘。 列隊(duì)可以是串行也可以是并發(fā)坡倔,串行列隊(duì)按順序執(zhí)行,并發(fā)列隊(duì)會(huì)并發(fā)執(zhí)行任務(wù)脖含,但是我們并不知道具體任務(wù)的執(zhí)行順序罪塔。
列隊(duì)的分類
系統(tǒng)列隊(duì)
主列隊(duì)
let mainQueue = DispatchQueue.main
全局列隊(duì)
let globalQueue = DispatchQueue.global()
用戶創(chuàng)建列隊(duì)
創(chuàng)建自己的列隊(duì),簡(jiǎn)單的方式就是指定列隊(duì)的名稱即可
let queue = DispatchQueue(label: "com.conpanyName.queue")
這樣的初始化的列隊(duì)有著默認(rèn)的配置項(xiàng),默認(rèn)的列隊(duì)是串行列隊(duì)养葵。便捷構(gòu)造函數(shù)如下
public convenience init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)
我們也可以自己顯示設(shè)置相關(guān)屬性征堪,創(chuàng)建一個(gè)并發(fā)列隊(duì)
? ? let label = "com.conpanyName.queue"
? ? let qos = DispatchQoS.default
? ? let attributes = DispatchQueue.Attributes.concurrent
? ? let autoreleaseFrequnecy = DispatchQueue.AutoreleaseFrequency.never
? ? let queue = DispatchQueue(label: label, qos: qos, attributes: attributes, autoreleaseFrequency: autoreleaseFrequnecy, target: nil)
參數(shù)介紹
label:列隊(duì)的標(biāo)識(shí)符,能夠方便區(qū)分列隊(duì)進(jìn)行調(diào)試
qos:列隊(duì)的優(yōu)先級(jí)(quality of service)关拒,其值如下:
? ? ? public struct DispatchQoS : Equatable {
? ? ? ? ? ? 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
? ? ? }
優(yōu)先級(jí)由最低的background到最高的userInteractive共五個(gè)佃蚜,還有一個(gè)為定義的unspecified.
background:最低優(yōu)先級(jí),等同于DISPATCH_QUEUE_PRIORITY_BACKGROUND. 用戶不可見着绊,比如:在后臺(tái)存儲(chǔ)大量數(shù)據(jù)
utility:優(yōu)先級(jí)等同于DISPATCH_QUEUE_PRIORITY_LOW谐算,可以執(zhí)行很長(zhǎng)時(shí)間,再通知用戶結(jié)果归露。比如:下載一個(gè)大文件洲脂,網(wǎng)絡(luò),計(jì)算
default:默認(rèn)優(yōu)先級(jí),優(yōu)先級(jí)等同于DISPATCH_QUEUE_PRIORITY_DEFAULT剧包,建議大多數(shù)情況下使用默認(rèn)優(yōu)先級(jí)
userInitiated:優(yōu)先級(jí)等同于DISPATCH_QUEUE_PRIORITY_HIGH,需要立刻的結(jié)果
.userInteractive:用戶交互相關(guān)恐锦,為了好的用戶體驗(yàn),任務(wù)需要立馬執(zhí)行疆液。使用該優(yōu)先級(jí)用于UI更新一铅,事件處理和小工作量任務(wù),在主線程執(zhí)行堕油。
Qos指定了列隊(duì)工作的優(yōu)先級(jí)潘飘,系統(tǒng)會(huì)根據(jù)優(yōu)先級(jí)來(lái)調(diào)度工作,越高的優(yōu)先級(jí)能夠越快被執(zhí)行掉缺,但是也會(huì)消耗功能福也,所以準(zhǔn)確的指定優(yōu)先級(jí)能夠保證app有效的使用資源。詳細(xì)可以看這里
attributes:列隊(duì)的屬性攀圈,也可以說(shuō)是類型暴凑,即是并發(fā)還是串行。attributes是一個(gè)結(jié)構(gòu)體并遵守OptionSet協(xié)議赘来,所以傳入的參數(shù)可以為[.option1, .option2]
? ? public struct Attributes : OptionSet {
? ? ? ? ? public let rawValue: UInt64
? ? ? ? ? public init(rawValue: UInt64)
? ? ? ? ? public static let concurrent: DispatchQueue.Attributes
? ? ? ? ? public static let initiallyInactive: DispatchQueue.Attributes
? ? }
默認(rèn):列隊(duì)是串行的
.concurrent:列隊(duì)是并發(fā)的
.initiallyInactive:列隊(duì)不會(huì)自動(dòng)執(zhí)行现喳,需要開發(fā)中手動(dòng)觸發(fā)
autoreleaseFrequency:自動(dòng)釋放頻率凯傲,有些列隊(duì)會(huì)在執(zhí)行完任務(wù)之后自動(dòng)釋放,有些是不會(huì)自動(dòng)釋放的嗦篱,需要手動(dòng)釋放冰单。
簡(jiǎn)單看一下列隊(duì)優(yōu)先級(jí)
? ? DispatchQueue.global(qos: .background).async {
? ? ? ? for i in 1...5 {
? ? ? ? ? ? print("background: \(i)")
? ? ? ? }
? ? }
? ? DispatchQueue.global(qos: .default).async {
? ? ? ? for i in 1...5 {
? ? ? ? ? ? print("default: \(i)")
? ? ? ? }
? ? }
? ? DispatchQueue.global(qos: .userInteractive).async {
? ? ? ? for i in 1...5 {
? ? ? ? ? ? print("userInteractive: \(i)")
? ? ? ? }
? ? }
? ? 執(zhí)行結(jié)果:
? ? ? ? ? ? default: 1
? ? ? ? ? ? userInteractive: 1
? ? ? ? ? ? background: 1
? ? ? ? ? ? default: 2
? ? ? ? ? ? userInteractive: 2
? ? ? ? ? ? background: 2
? ? ? ? ? ? userInteractive: 3
? ? ? ? ? ? default: 3
? ? ? ? ? ? userInteractive: 4
? ? ? ? ? ? userInteractive: 5
? ? ? ? ? ? default: 4
? ? ? ? ? ? background: 3
? ? ? ? ? ? default: 5
? ? ? ? ? ? background: 4
? ? ? ? ? ? background: 5
DispatchWorkItem
DispatchWorkItem是用于幫助DispatchQueue來(lái)執(zhí)行列隊(duì)中的任務(wù)。類的相關(guān)內(nèi)容如下:
? ? public class DispatchWorkItem {
? ? ? ? ? public init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, block: @escaping @convention(block) () -> Swift.Void)
? ? ? ? ? public func perform()
? ? ? ? ? public func wait()
? ? ? ? ? public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
? ? ? ? ? public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
? ? ? ? ? public func notify(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, queue: DispatchQueue, execute: @escaping @convention(block) () -> Swift.Void)
? ? ? ? ? public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
? ? ? ? ? public func cancel()
? ? ? ? ? public var isCancelled: Bool { get }
? ? }
一般情況下灸促,我們開啟一個(gè)異步線程诫欠,會(huì)這樣創(chuàng)建列隊(duì)并執(zhí)行async方法,以閉包的方式提交任務(wù)浴栽。
? ? DispatchQueue.global().async {
? ? ? ? // do async task
? ? }
但是Swift3中使用了DispatchWorkItem類將任務(wù)封裝成為對(duì)象荒叼,由對(duì)象進(jìn)行任務(wù)。
? ? let item = DispatchWorkItem {
? ? ? ? ? // do task
? ? }
? ? DispatchQueue.global().async(execute: item)
當(dāng)然典鸡,這里也可以使用DispatchWorkItem實(shí)例對(duì)象的perform方法執(zhí)行任務(wù)
? ? let workItem = DispatchWorkItem {
? ? ? ? // do task
? ? }
? ? DispatchQueue.global().async {
? ? ? ? workItem.perform()
? ? }
但是對(duì)比一下兩種方式被廓,顯然第一種更加簡(jiǎn)潔,方便萝玷。
執(zhí)行任務(wù)結(jié)束通過(guò)nofify獲得通知
? ? let workItem = DispatchWorkItem {
? ? ? ? // do async task
? ? ? ? print(Thread.current)
? ? }
? ? DispatchQueue.global().async {
? ? ? ? workItem.perform()
? ? }
? ? workItem.notify(queue: DispatchQueue.main) {
? ? ? ? // update UI
? ? ? ? print(Thread.current)
? ? }
使用wait等待任務(wù)執(zhí)行完成
? ? let queue = DispatchQueue(label: "queue", attributes: .concurrent)
? ? let workItem = DispatchWorkItem {
? ? ? ? sleep(5)
? ? ? ? print("done")
? ? }
? ? queue.async(execute: workItem)
? ? print("before waiting")
? ? workItem.wait()
? ? print("after waiting")
? ? 執(zhí)行結(jié)果:
? ? ? ? ? ? before waiting
? ? ? ? ? ? done
? ? ? ? ? ? after waiting
也可以在初始化的時(shí)候指定更多的參數(shù)
? ? let item = DispatchWorkItem(qos: .default, flags: .barrier) {
? ? ? ? // do task
? ? }
第一個(gè)參數(shù)同樣說(shuō)優(yōu)先級(jí)嫁乘,第二個(gè)參數(shù)指定flag
? ? public struct DispatchWorkItemFlags : OptionSet, RawRepresentable {
? ? ? ? public let rawValue: UInt
? ? ? ? public init(rawValue: UInt)
? ? ? ? public static let barrier: DispatchWorkItemFlags
? ? ? ? public static let detached: DispatchWorkItemFlags
? ? ? ? public static let assignCurrentContext: DispatchWorkItemFlags
? ? ? ? public static let noQoS: DispatchWorkItemFlags
? ? ? ? public static let inheritQoS: DispatchWorkItemFlags
? ? ? ? public static let enforceQoS: DispatchWorkItemFlags
? ? }
barrier
假如我們有一個(gè)并發(fā)的列隊(duì)用來(lái)讀寫一個(gè)數(shù)據(jù)對(duì)象,如果這個(gè)列隊(duì)的操作是讀球碉,那么可以同時(shí)多個(gè)進(jìn)行蜓斧。如果有寫的操作,則必須保證在執(zhí)行寫操作時(shí)睁冬,不會(huì)有讀取的操作執(zhí)行法精,必須等待寫操作完成之后再開始讀取操作,否則會(huì)造成讀取的數(shù)據(jù)出錯(cuò)痴突,經(jīng)典的讀寫問(wèn)題。這里我們就可以使用barrier:
? ? let item = DispatchWorkItem(qos: .default, flags: .barrier) {
? ? ? ? // write data
? ? }
? ? let dataQueue = DispatchQueue(label: "com.data.queue", attributes: .concurrent)
? ? dataQueue.async(execute: item)
字典的讀寫操作
? ? private let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
? ? private var dictionary: [String: Any] = [:]
? ? public func set(_ value: Any?, forKey key: String) {
? ? ? ? // .barrier flag ensures that within the queue all reading is done
? ? ? ? // before the below writing is performed and
? ? ? ? // pending readings start after below writing is performed
? ? ? ? concurrentQueue.async(flags: .barrier) {
? ? ? ? ? ? ? self.dictionary[key] = value
? ? ? ? }
? ? }
? ? public func object(forKey key: String) -> Any? {
? ? ? ? var result: Any?
? ? ? ? concurrentQueue.sync {
? ? ? ? ? ? result = dictionary[key]
? ? ? ? }
? ? ? ? // returns after concurrentQueue is finished operation
? ? ? ? // beacuse concurrentQueue is run synchronously
? ? ? ? return result
? ? }
通過(guò)在并發(fā)代碼中使用barrier將能夠保證寫操作在所有讀取操作完成之后進(jìn)行狼荞,而且確保寫操作執(zhí)行完成之后再開始后續(xù)的讀取操作辽装。具體的詳情看這里
延時(shí)處理
使用asyncAfter來(lái)提交任務(wù)進(jìn)行延遲。之前是使用dispatch_time,現(xiàn)在是使用DispatchTime對(duì)象表示相味∈盎可以使用靜態(tài)方法now獲得當(dāng)前時(shí)間,然后再通過(guò)加上DispatchTimeInterval枚舉獲得一個(gè)需要延遲的時(shí)間丰涉。注意:僅僅是用于在具體時(shí)間執(zhí)行任務(wù)拓巧,不要在資源競(jìng)爭(zhēng)的情況下使用。并且在主列隊(duì)使用一死。
? ? let delay = DispatchTime.now() + DispatchTimeInterval.seconds(10)
? ? DispatchQueue.main.asyncAfter(deadline: delay) {
? ? ? ? // 延遲執(zhí)行
? ? }
我們可以進(jìn)一步簡(jiǎn)化肛度,直接添加時(shí)間
? ? let delay = DispatchTime.now() + 10
? ? DispatchQueue.main.asyncAfter(deadline: delay) {
? ? ? ? // 延遲執(zhí)行
? ? }
因?yàn)樵贒ispatchTime中自定義了“+”號(hào)。
public func +(time: DispatchTime, seconds: Double) -> DispatchTime
更多有關(guān)延時(shí)操作看這里
DispatchGroup
DispatchGroup用于管理一組任務(wù)的執(zhí)行投慈,然后監(jiān)聽任務(wù)的完成承耿,進(jìn)而執(zhí)行后續(xù)操作冠骄。比如:同一個(gè)頁(yè)面發(fā)送多個(gè)網(wǎng)絡(luò)請(qǐng)求,等待所有結(jié)果請(qǐng)求成功刷新UI界面加袋。一般的操作如下:
? ? let queue = DispatchQueue.global()
? ? let group = DispatchGroup()
? ? queue.async(group: group) {
? ? ? ? print("Task one finished")
? ? }
? ? queue.async(group: group) {
? ? ? ? print("Task two finished")
? ? }
? ? queue.async(group: group) {
? ? ? ? print("Task three finished")
? ? }
? ? group.notify(queue: queue) {
? ? ? ? print("All task has finished")
? ? }
打印如下:
Task three finished
Task two finished
Task one finished
All task has finished
由于是并發(fā)執(zhí)行異步任務(wù)凛辣,所以任務(wù)的先后次序是不一定的,看起來(lái)符合我們的需求职烧,最后接受通知然后可以刷新UI操作扁誓。但是真實(shí)的網(wǎng)絡(luò)請(qǐng)求是異步、耗時(shí)的蚀之,并不是立馬就返回蝗敢,所以我們使用asyncAfter模擬延時(shí)看看,將任務(wù)1延時(shí)一秒執(zhí)行:
? ? let queue = DispatchQueue.global()
? ? let group = DispatchGroup()
? ? queue.async(group: group) {
? ? ? ? DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
? ? ? ? ? ? print("Task one finished")
? ? ? ? })
? ? }
? ? queue.async(group: group) {
? ? ? ? print("Task two finished")
? ? }
? ? queue.async(group: group) {
? ? ? ? print("Task three finished")
? ? }
? ? group.notify(queue: queue) {
? ? ? ? print("All task has finished")
? ? }
結(jié)果卻不是我們預(yù)期的那樣恬总,輸出結(jié)果如下:
Task two finished
Task three finished
All task has finished
Task one finished
所以前普,為了真正實(shí)現(xiàn)預(yù)期的效果,我們需要配合group的enter和leave兩個(gè)函數(shù)壹堰。每次執(zhí)行g(shù)roup.enter()表示一個(gè)任務(wù)被加入到列隊(duì)組group中拭卿,此時(shí)group中的任務(wù)的引用計(jì)數(shù)會(huì)加1,當(dāng)使用group.leave() 贱纠,表示group中的一個(gè)任務(wù)完成峻厚,group中任務(wù)的引用計(jì)數(shù)減1.當(dāng)group列隊(duì)組里面的任務(wù)引用計(jì)數(shù)為0時(shí),會(huì)通知notify函數(shù)谆焊,任務(wù)執(zhí)行完成惠桃。注意:enter()和leave()成對(duì)出現(xiàn)的。
? ? ? let queue = DispatchQueue.global()
? ? ? let group = DispatchGroup()
? ? ? group.enter()
? ? ? queue.async(group: group) {
? ? ? ? ? DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
? ? ? ? ? ? ? print("Task one finished")
? ? ? ? ? ? ? group.leave()
? ? ? ? ? })
? ? ? }
? ? ? group.enter()
? ? ? queue.async(group: group) {
? ? ? ? ? print("Task two finished")
? ? ? ? ? group.leave()
? ? ? }
? ? ? group.enter()
? ? ? queue.async(group: group) {
? ? ? ? ? print("Task three finished")
? ? ? ? ? group.leave()
? ? ? }
? ? ? group.notify(queue: queue) {
? ? ? ? ? print("All task has finished")
? ? ? }
這下OK了辖试,輸出跟預(yù)期一樣辜王。當(dāng)然這里也可以使用信號(hào)量實(shí)現(xiàn),后面會(huì)介紹罐孝。
Task three finished
Task two finished
Task one finished
All task has finished
信號(hào)量
對(duì)于信號(hào)量的具體內(nèi)容呐馆,可以看我之前寫的一篇博文。使用起來(lái)很簡(jiǎn)單莲兢,創(chuàng)建信號(hào)量對(duì)象汹来,調(diào)用signal方法發(fā)送信號(hào),信號(hào)加1改艇,調(diào)用wait方法等待收班,信號(hào)減1.現(xiàn)在也適用信號(hào)量實(shí)現(xiàn)剛剛的多個(gè)請(qǐng)求功能。
? ? ? let queue = DispatchQueue.global()
? ? ? let group = DispatchGroup()
? ? ? let semaphore = DispatchSemaphore(value: 0)
? ? ? queue.async(group: group) {
? ? ? ? ? DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
? ? ? ? ? ? ? semaphore.signal()
? ? ? ? ? ? ? print("Task one finished")
? ? ? ? ? })
? ? ? ? ? semaphore.wait()
? ? ? }
? ? ? queue.async(group: group) {
? ? ? ? ? DispatchQueue.main.asyncAfter(deadline: .now() + 0.8, execute: {
? ? ? ? ? ? ? semaphore.signal()
? ? ? ? ? ? ? print("Task two finished")
? ? ? ? ? })
? ? ? ? ? semaphore.wait()
? ? ? }
? ? ? queue.async(group: group) {
? ? ? ? ? print("Task three finished")
? ? ? }
? ? ? group.notify(queue: queue) {
? ? ? ? ? print("All task has finished")
? ? ? }
Suspend / Resume
Suspend可以掛起一個(gè)線程谒兄,即暫停線程摔桦,但是仍然暫用資源,只是不執(zhí)行
Resume回復(fù)線程承疲,即繼續(xù)執(zhí)行掛起的線程酣溃。
循環(huán)執(zhí)行任務(wù)
之前使用GCD的dispatch_apply()執(zhí)行多次任務(wù)瘦穆,現(xiàn)在是調(diào)用concurrentPerform(),下面是并發(fā)執(zhí)行5次
? ? DispatchQueue.concurrentPerform(iterations: 5) {
? ? ? ? print("\($0)")
? ? }
DispatchSource
DispatchSource提高了相關(guān)的API來(lái)監(jiān)控低級(jí)別的系統(tǒng)對(duì)象,比如:Mach ports, Unix descriptors, Unix signals, VFS nodes赊豌。并且能夠異步提交事件到派發(fā)列隊(duì)執(zhí)行扛或。
簡(jiǎn)單定時(shí)器
? ? // 定時(shí)時(shí)間
? ? var timeCount = 60
? ? // 創(chuàng)建時(shí)間源
? ? let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
? ? timer.schedule(deadline: .now(), repeating: .seconds(1))
? ? timer.setEventHandler {
? ? ? ? timeCount -= 1
? ? ? ? if timeCount <= 0 { timer.cancel() }
? ? ? ? DispatchQueue.main.async {
? ? ? ? ? ? // update UI or other task
? ? ? ? }
? ? }
? ? // 啟動(dòng)時(shí)間源
? ? timer.resume()
對(duì)于比使用Timer的好處可以看這里
應(yīng)用場(chǎng)景
多個(gè)任務(wù)依次執(zhí)行
最容易想到的就是創(chuàng)建一個(gè)串行列隊(duì),然后添加任務(wù)到列隊(duì)執(zhí)行碘饼。
? ? let serialQueue = DispatchQueue(label: "com.my.queue")
? ? serialQueue.async {
? ? ? print("task one")
? ? }
? ? serialQueue.async {
? ? ? print("task two")
? ? }
? ? serialQueue.async {
? ? ? print("task three")
? ? }
其次就是使用前面講到的DispatchGroup熙兔。
取消DispatchWorkItem的任務(wù)
直接取消任務(wù)
? ? let queue = DispatchQueue(label: "queue", attributes: .concurrent)
? ? let workItem = DispatchWorkItem {
? ? ? ? print("done")
? ? }
? ? DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
? ? ? ? queue.async(execute: workItem) // not work
? ? }
? ? workItem.cancel()
直接調(diào)用取消,異步任務(wù)不會(huì)執(zhí)行艾恼。
執(zhí)行的過(guò)程中取消任務(wù)
? ? func cancelWork() {
? ? ? ? let queue = DispatchQueue.global()
? ? ? ? var item: DispatchWorkItem!
? ? ? ? // create work item
? ? ? ? item = DispatchWorkItem { [weak self] in
? ? ? ? ? ? ? for i in 0 ... 10_000_000 {
? ? ? ? ? ? ? ? ? if item.isCancelled { break }
? ? ? ? ? ? ? ? ? print(i)
? ? ? ? ? ? ? ? ? self?.heavyWork()
? ? ? ? ? ? ? }
? ? ? ? ? ? ? item = nil? ? // resolve strong reference cycle
? ? ? ? }
? ? ? ? // start it
? ? ? ? queue.async(execute: item)
? ? ? ? // after five seconds, stop it if it hasn't already
? ? ? ? queue.asyncAfter(deadline: .now() + 5) { [weak item] in
? ? ? ? ? ? ? ? item?.cancel()
? ? ? ? }
? ? }
具體詳情看這里住涉,也可以了解這篇文章
注意事項(xiàng)
線程死鎖
不要在主列隊(duì)中執(zhí)行同步任務(wù),這樣會(huì)造成死鎖問(wèn)題钠绍。
————————————————
版權(quán)聲明:本文為CSDN博主「Longshihua」的原創(chuàng)文章舆声,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明柳爽。
原文鏈接:https://blog.csdn.net/longshihua/article/details/79756676