Swift3.0 DispatchQueue-對(duì)GCD的改寫

在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解析

  1. 便利構(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("都干完了,可以下班了")
    }

分析:queue1queue2是異步執(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ì)列")
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容