Swift3.0 DispatchQueue-對GCD的改寫

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

  1. 便利構造器

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

分析:queue1queue2是異步執行的兩個隊列,queue1中令系統沉睡2s,方法group.wait()的作用是讓queue1執行完畢后再執行后續隊列(這里是queue2),否則queue2異步執行。
group.notify()方法是在group內的隊列都執行完畢后才會執行。

4,獲取主線程隊列

 let mainQueue = DispatchQueue.main
 mainQueue.async { 
        print("其實還是主線程")
    }

5,全局隊列

DispatchQueue.global().async {
    print("全局隊列")
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容