Swift006-多線程

Swift006-多線程

相關(guān)概念

  • 進(jìn)程
    指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,進(jìn)程擁有獨(dú)立運(yùn)行所需的全部資源(例如:正在運(yùn)行的QQ就是一個(gè)進(jìn)程)。

  • 線程
    指程序中獨(dú)立運(yùn)行的代碼段(例如:接收QQ消息的代碼),一個(gè)進(jìn)程是由一或多個(gè)線程組成。

  • 多線程
    1個(gè)進(jìn)程中可以開啟多條線程,每條線程可以并行(同時(shí))執(zhí)行不同的任務(wù),進(jìn)程只負(fù)責(zé)資源的調(diào)度和分配,線程才是程序真正的執(zhí)行單元,負(fù)責(zé)代碼的執(zhí)行。

  • 單線程與多線程對比

  • 單線程程序:只有一個(gè)線程,代碼順序執(zhí)行,容易出現(xiàn)代碼阻塞(頁面假死)。
  • 多線程程序:有多個(gè)線程,線程間獨(dú)立運(yùn)行,能有效的避免代碼阻塞,并且提高程序的運(yùn)行性能。
  • 線程相關(guān)
  • 同步線程:同步線程會阻塞當(dāng)前線程去執(zhí)行線程內(nèi)的任務(wù),執(zhí)行完之后才會反回當(dāng)前線程。
  • 異步線程:異步線程不會阻塞當(dāng)前線程,會開啟其他線程去執(zhí)行線程內(nèi)的任務(wù)。
  • 串行隊(duì)列:線程任務(wù)按先后順序逐個(gè)執(zhí)行(需要等待隊(duì)列里面前面的任務(wù)執(zhí)行完之后再執(zhí)行新的任務(wù))。
  • 并發(fā)隊(duì)列:多個(gè)任務(wù)按添加順序一起開始執(zhí)行(不用等待前面的任務(wù)執(zhí)行完再執(zhí)行新的任務(wù)),但是添加間隔往往忽略不計(jì),所以看著像是一起執(zhí)行的。
  • 并發(fā)VS并行:并行是基于多核設(shè)備的,并行一定是并發(fā),并發(fā)不一定是并行。
  • 死鎖
    死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去

例如主線程串行隊(duì)列同步執(zhí)行任務(wù)引起死鎖

DispatchQueue.main.sync {
    print("死鎖了不執(zhí)行")
}

自定義串行隊(duì)列同步任務(wù) 嵌套 該自定義串行隊(duì)列同步任務(wù),產(chǎn)生死鎖

let serialQueue = DispatchQueue(label: "com.ddy.serialQueue")
serialQueue.sync {
    print("執(zhí)行了1")
    serialQueue.sync {
        print("死鎖了不執(zhí)行")
    }
}

自定義串行隊(duì)列異步任務(wù) 嵌套 該自定義串行隊(duì)列同步任務(wù), 產(chǎn)生死鎖

let serialQueue = DispatchQueue(label: "com.ddy.serialQueue")
serialQueue.async {
    print("執(zhí)行了1")
    serialQueue.sync {
        print("死鎖了不執(zhí)行")
    }
}
print("執(zhí)行了3")

總結(jié):遇到串行同步要小心

  • 死鎖的四個(gè)必要條件
  • 互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用。
  • 請求與保持條件:一個(gè)進(jìn)程因請求資源而阻塞時(shí),對已獲得的資源保持不放。
  • 不剝奪條件:進(jìn)程已獲得的資源,在末使用完之前,不能強(qiáng)行剝奪。
  • 循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
  • 避免死鎖的方法
  1. 破壞“互斥”條件:就是在系統(tǒng)里取消互斥。若資源不被一個(gè)進(jìn)程獨(dú)占使用,那么死鎖是肯定不會發(fā)生的。但一般“互斥”條件是無法破壞的。因此,在死鎖預(yù)防里主要是破壞其他三個(gè)必要條件,而不去涉及破壞“互斥”條件。
  2. 破壞“請求和保持”條件:在系統(tǒng)中不允許進(jìn)程在已獲得某種資源的情況下,申請其他資源。即要想出一個(gè)辦法,阻止進(jìn)程在持有資源的同時(shí)申請其他資源。
    方法:要求每個(gè)進(jìn)程提出新的資源申請前,釋放它所占有的資源。這樣,一個(gè)進(jìn)程在需要資源S時(shí),須先把它先前占有的資源R釋放掉,然后才能提出對S的申請,即使它可能很快又要用到資源R。
  3. 破壞“不可搶占”條件:允許對資源實(shí)行搶奪。
    方法一:如果占有某些資源的一個(gè)進(jìn)程進(jìn)行進(jìn)一步資源請求被拒絕,則該進(jìn)程必須釋放它最初占有的資源,如果有必要,可再次請求這些資源和另外的資源。
    方法二:如果一個(gè)進(jìn)程請求當(dāng)前被另一個(gè)進(jìn)程占有的一個(gè)資源,則操作系統(tǒng)可以搶占另一個(gè)進(jìn)程,要求它釋放資源。只有在任意兩個(gè)進(jìn)程的優(yōu)先級都不相同的條件下,該方法才能預(yù)防死鎖。
  4. 破壞“循環(huán)等待”條件:將系統(tǒng)中的所有資源統(tǒng)一編號,進(jìn)程可在任何時(shí)刻提出資源申請,但所有申請必須按照資源的編號順序(升序)提出。這樣做就能保證系統(tǒng)不出現(xiàn)死鎖。
    方法:利用銀行家算法避免死鎖。

死鎖參考

  • 線程安全

    一段線程安全的代碼(對象),可以同時(shí)被多個(gè)線程或并發(fā)的任務(wù)調(diào)度,不會產(chǎn)生問題,非線程安全的只能按次序被訪問。所有Mutable對象都是非線程安全的,所有Immutable對象都是線程安全的,使用Mutable對象,一定要用同步鎖來同步訪問(@synchronized)。

    互斥鎖:能夠防止多線程搶奪造成的數(shù)據(jù)安全問題,但是需要消耗大量的資源
    原子屬性(atomic)加鎖

    atomic: 原子屬性,為setter方法加鎖,將屬性以atomic的形式來聲明,該屬性變量就能支持互斥鎖了。

    nonatomic: 非原子屬性,不會為setter方法加鎖,聲明為該屬性的變量,客戶端應(yīng)盡量避免多線程爭奪同一資源。

幾種多線程技術(shù)比較

  • pthread
    優(yōu)點(diǎn): 跨平臺,可移植性強(qiáng)
    缺點(diǎn): 程序員管理生命周期,使用難度大。一般不用

  • NSThread (抽象層次:低)
    優(yōu)點(diǎn):輕量級,簡單易用,可以直接操作線程對象
    缺點(diǎn): 需要自己管理線程的生命周期,線程同步。線程同步對數(shù)據(jù)的加鎖會有一定的系統(tǒng)開銷。

  • Cocoa NSOperation (抽象層次:中)
    優(yōu)點(diǎn):不需要關(guān)心線程管理,數(shù)據(jù)同步的事情,可以把精力放在學(xué)要執(zhí)行的操作上?;贕CD,是對GCD 的封裝,比GCD更加面向?qū)ο?br> 缺點(diǎn): NSOperation是個(gè)抽象類,使用它必須使用它的子類,可以實(shí)現(xiàn)它或者使用它定義好的兩個(gè)子類NSInvocationOperation、NSBlockOperation.

  • GCD 全稱Grand Center Dispatch (抽象層次:高)
    優(yōu)點(diǎn):是 Apple 開發(fā)的一個(gè)多核編程的解決方法,簡單易用,效率高,速度快,基于C語言,更底層更高效,并且不是Cocoa框架的一部分,自動管理線程生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)。
    缺點(diǎn): 使用GCD的場景如果很復(fù)雜,就有非常大的可能遇到死鎖問題。

GCD抽象層次最高,使用也簡單,因此,蘋果也推薦使用GCD

  • 為什么要用 GCD 呢?
    • GCD 可用于多核的并行運(yùn)算
    • GCD 會自動利用更多的 CPU 內(nèi)核(比如雙核、四核)
    • GCD 會自動管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
    • 程序員只需要告訴 GCD 想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼

GCD

1.主線程串行隊(duì)列(main queue):提交至Main queue的任務(wù)會在主線程中執(zhí)行,Main queue 可以通過DispatchQueue.main來獲取,主隊(duì)列一定伴隨主線程,但主線程不一定伴隨主隊(duì)列。
2.全局并發(fā)隊(duì)列(Global queue):全局并發(fā)隊(duì)列由整個(gè)進(jìn)程共享,有Qos優(yōu)先級別。Global queue 可以通過調(diào)用DispatchQueue.global()函數(shù)來獲?。梢栽O(shè)置優(yōu)先級)
3.自定義隊(duì)列(Custom queue): 可以為串行,也可以為并發(fā)。Custom queue 可以通過DispatchQueue(label: String)和DispatchQueue(label: String, attributes: DispatchQueue.Attributes.concurrent)來獲?。?br> 4.隊(duì)列組 (Group queue):將多線程進(jìn)行分組,最大的好處是可獲知所有線程的完成情況。
Group queue 可以通過調(diào)用dispatch_group_create()來創(chuàng)建,通過dispatch_group_notify 可以直接監(jiān)聽組里所有線程完成情況。

  • 主線程串行隊(duì)列(The main queue)

    1.主線程串行隊(duì)列同步執(zhí)行任務(wù),在主線程運(yùn)行時(shí),會產(chǎn)生死鎖

    DispatchQueue.main.sync {
        print("死鎖了不執(zhí)行")
    }
    

    2.主線程串行隊(duì)列異步執(zhí)行任務(wù),在主線程運(yùn)行,不會產(chǎn)生死鎖

    DispatchQueue.main.async {
        print("執(zhí)行了")
    }
    

    3.安全異步主線程主隊(duì)列

    import Foundation
    
    extension DispatchQueue {
        fileprivate static var currentQueueLabel: String? {
        let cString = __dispatch_queue_get_label(nil)
        return String(cString: cString)
        }
        // "com.apple.main-thread"
        fileprivate static var isMainQueue: Bool {
        return currentQueueLabel == self.main.label
        }
    }    
    
    func ddyMainAsyncSafe(_ execute: @escaping () -> Void) {
        DispatchQueue.isMainQueue ? execute() : DispatchQueue.main.async(execute: execute)
    }
    
    // 調(diào)用
    ddyMainAsyncSafe {
        print("主線程主隊(duì)列刷新UI")
    }   
    

    附:RxSwift中判斷主線程主隊(duì)列方式

    extension DispatchQueue {
        private static var token: DispatchSpecificKey<()> = {
            let key = DispatchSpecificKey<()>()
            DispatchQueue.main.setSpecific(key: key, value: ())
            return key
        }()
    
        static var isMain: Bool {
            return DispatchQueue.getSpecific(key: token) != nil
        }
    }
    

    注意:主線程串行隊(duì)列由系統(tǒng)默認(rèn)生成,無法調(diào)用conQueue.resume()和Queue.suspend()來控制執(zhí)行繼續(xù)或中斷。

  • 全局并發(fā)隊(duì)列(Global queue)

    耗時(shí)操作(如網(wǎng)絡(luò)請求,IO,數(shù)據(jù)庫讀寫等)在子線程中處理,然后通知主線程更新界面

    1.全局并發(fā)隊(duì)列同步執(zhí)行任務(wù),在主線程執(zhí)行會導(dǎo)致頁面卡頓。

    print("同步執(zhí)行任務(wù) 1")
    DispatchQueue.global().sync {
        print("同步執(zhí)行任務(wù) 2")
    }
    print("同步執(zhí)行任務(wù) 3")
    

    2 全局并發(fā)隊(duì)列異步執(zhí)行任務(wù),會開啟新的子線程去執(zhí)行任務(wù),頁面不會卡頓。

    print("異步執(zhí)行任務(wù) 1")
    DispatchQueue.global().async {
        print("異步執(zhí)行任務(wù) 2")
    }
    print("異步執(zhí)行任務(wù) 3")   
    

    3 多個(gè)全局并發(fā)隊(duì)列,異步執(zhí)行任務(wù)。

    print("全局并發(fā)隊(duì)列 異步執(zhí)行任務(wù) 1")
    DispatchQueue.global().async {
        print("全局并發(fā)隊(duì)列 異步執(zhí)行任務(wù) 2")
    }
    DispatchQueue.global().async {
        print("全局并發(fā)隊(duì)列 異步執(zhí)行任務(wù) 3")
    }
    print("全局并發(fā)隊(duì)列 異步執(zhí)行任務(wù) 4")
    

    異步線程的執(zhí)行順序是不確定的,幾乎同步開始執(zhí)行,2和3 順序不確定
    全局并發(fā)隊(duì)列由系統(tǒng)默認(rèn)生成,無法調(diào)用conQueue.resume()和Queue.suspend()來控制執(zhí)行繼續(xù)或中斷。

    // Swift3開始使用了DispatchWorkItem類將任務(wù)封裝成為對象,由對象進(jìn)行任務(wù)。
    let item = DispatchWorkItem {
        // do task
    }
    DispatchQueue.global().async(execute: item)
    
    // 也可以使用DispatchWorkItem實(shí)例對象的perform方法執(zhí)行任務(wù)
    let workItem = DispatchWorkItem {
        // do task
    }
    DispatchQueue.global().async {
        workItem.perform()
    }
    
  • 自定義隊(duì)列 (Custom queue)

    1 自定義串行隊(duì)列同步執(zhí)行任務(wù)(依次執(zhí)行)

    let serialQueue = DispatchQueue(label: "com.ddy.serialQueue")
    print("自定義串行隊(duì)列同步執(zhí)行任務(wù) 1")
    serialQueue.async {
        print("自定義串行隊(duì)列同步執(zhí)行任務(wù) 2")
    }
    serialQueue.async {
        print("自定義串行隊(duì)列同步執(zhí)行任務(wù) 3")
    }
    print("自定義串行隊(duì)列同步執(zhí)行任務(wù) 4")
    

    2 自定義串行隊(duì)列同步任務(wù) 嵌套 該自定義串行隊(duì)列同步任務(wù),產(chǎn)生死鎖

    let serialQueue = DispatchQueue(label: "com.ddy.serialQueue")
    serialQueue.sync {
        print("執(zhí)行了1")
        serialQueue.sync {
            print("死鎖了不執(zhí)行")
        }
    }
    print("沒執(zhí)行")
    

    3 自定義串行隊(duì)列同步任務(wù)嵌套并發(fā)隊(duì)列同步任務(wù) 不死鎖

    let serialQueue = DispatchQueue(label: "com.ddy.serialQueue")
    let globalQueue = DispatchQueue.global()
    let concurrentQ = DispatchQueue(label: "concurrentQueue1", attributes: .concurrent)
    serialQueue.sync {
        print("執(zhí)行了1")
        globalQueue.sync {
            print("執(zhí)行了2")
        }
        concurrentQ.sync {
            print("執(zhí)行了3")
        }
    }
    print("執(zhí)行4")
    

    4 自定義串行隊(duì)列同步執(zhí)行任務(wù) 嵌套 另一個(gè)自定義串行隊(duì)列同步任務(wù) 不死鎖

    let serialQueue1 = DispatchQueue(label: "com.ddy.serialQueue1")
    let serialQueue2 = DispatchQueue(label: "com.ddy.serialQueue2")
    serialQueue1.sync {
        print("執(zhí)行了1")
        serialQueue2.sync {
            print("執(zhí)行了2")
        }
    }
    print("執(zhí)行了3")
    

    5 自定義串行隊(duì)列異步執(zhí)行任務(wù) 嵌套 該自定義串行隊(duì)列同步任務(wù),產(chǎn)生死鎖

    let serialQueue = DispatchQueue(label: "com.ddy.serialQueue")
    serialQueue.async {
        print("執(zhí)行了1")
        serialQueue.sync {
            print("死鎖了不執(zhí)行")
        }
    }
    print("執(zhí)行了3")
    

    總結(jié):遇到嵌套串行隊(duì)列同步任務(wù)要小心 可能 會死鎖

6 自定義并發(fā)隊(duì)列執(zhí)行同步任務(wù)(順序執(zhí)行)

```
let concurrentQ = DispatchQueue(label: "concurrentQueue1", attributes: .concurrent)
print("執(zhí)行了 1")
concurrentQ.sync {
    print("執(zhí)行了 2")
}
concurrentQ.sync {
    print("執(zhí)行了 3")
}
print("執(zhí)行了 4")
```

7 自定義并發(fā)隊(duì)列同步任務(wù) 嵌套 該自定義并發(fā)隊(duì)列同步任務(wù) (順序執(zhí)行 不會死鎖)

```
let concurrentQ = DispatchQueue(label: "concurrentQueue1", attributes: .concurrent)
    print("執(zhí)行了 1")
    concurrentQ.sync {
        print("執(zhí)行了 2")
        concurrentQ.sync {
            print("執(zhí)行了 3")
        }
    }
print("執(zhí)行了 4")
```

8 自定義并發(fā)隊(duì)列執(zhí)行異步任務(wù)(2,3不確定順序)

```
let concurrentQ = DispatchQueue(label: "concurrentQueue1", attributes: .concurrent)
print("執(zhí)行了 1")
concurrentQ.async {
    print("執(zhí)行了 2")
}
concurrentQ.async {
    print("執(zhí)行了 3")
}
print("執(zhí)行了 4")
```

隊(duì)列便利構(gòu)造器

- label: 隊(duì)列的唯一標(biāo)識符(用于調(diào)試)
- qos: 隊(duì)列的優(yōu)先級(.userInteractive .userInitiated .default .utility .background .unspecified)
    - .userInteractive:用戶交互相關(guān),為了好的用戶體驗(yàn),任務(wù)需要立馬執(zhí)行。使用該優(yōu)先級用于UI更新,事件處理和小工作量任務(wù),在主線程執(zhí)行。
    - .userInitiated:優(yōu)先級等同于DISPATCH_QUEUE_PRIORITY_HIGH,需要立刻的結(jié)果
    - .default:默認(rèn)優(yōu)先級,優(yōu)先級等同于DISPATCH_QUEUE_PRIORITY_DEFAULT,建議大多數(shù)情況下使用默認(rèn)優(yōu)先級
    - .utility:優(yōu)先級等同于DISPATCH_QUEUE_PRIORITY_LOW,可以執(zhí)行很長時(shí)間,再通知用戶結(jié)果。比如:下載一個(gè)大文件,網(wǎng)絡(luò),計(jì)算
    - .background:最低優(yōu)先級,等同于DISPATCH_QUEUE_PRIORITY_BACKGROUND. 用戶不可見,比如:在后臺存儲大量數(shù)據(jù)
    - .unspecified:未定義
- attributes: 隊(duì)列屬性(attributes是一個(gè)結(jié)構(gòu)體并遵守OptionSet協(xié)議,所以傳入的參數(shù)可以為[.option1, .option2])
    - .concurrent并發(fā)隊(duì)列,
    - .initiallyInactive表明隊(duì)列需要手動開啟。不填寫時(shí)默認(rèn)隊(duì)列串行、自動執(zhí)行
- autoreleaseFrequency:自動釋放頻率,有些列隊(duì)會在執(zhí)行完任務(wù)之后自動釋放,有些是不會自動釋放的,需要手動釋放。官方文檔是說當(dāng)設(shè)為.workItem時(shí),所有異步任務(wù)提交的代碼塊會被封裝成獨(dú)立的任務(wù),在同步執(zhí)行的隊(duì)列則不受影響。
- target:目標(biāo)隊(duì)列

```
// 隊(duì)列的便利構(gòu)造函數(shù)(便利構(gòu)造器)
public convenience init(label: String, qos: DispatchQoS = .unspecified, attributes: DispatchQueue.Attributes = [], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit, target: DispatchQueue? = nil)
// 優(yōu)先級順序:userInteractive> userInitiated> default> utility> background> unspecified

let label = "com.ddy.concurrentQueue"
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)
```

等待任務(wù)結(jié)束

```
let concurrentQ = DispatchQueue(label: "concurrentQueue1", attributes: .concurrent)
// 可以在初始化的時(shí)候指定更多的參數(shù)
let workItem = DispatchWorkItem(qos: .default, flags: .barrier) {
    sleep(5)
    print("done")
}
concurrentQ.async(execute: workItem)
print("before waiting")
workItem.wait()
print("after waiting")
// before waiting
// done
// after waiting
```
  • 隊(duì)列組(Group queue)

    場景1: A、B、C 三個(gè)任務(wù)并發(fā)異步執(zhí)行,都執(zhí)行完才執(zhí)行D任務(wù)(三個(gè)網(wǎng)絡(luò)請求和都請求完畢才刷新UI)

    // 并發(fā)隊(duì)列
    let concurrentQ = DispatchQueue(label: "com.ddy.concurrentQueue", attributes: .concurrent)
    // 創(chuàng)建組
    let group = DispatchGroup()
    // 網(wǎng)絡(luò)請求1
    group.enter()
    concurrentQ.async {
        DDYRequest.request(["test":"1"], { (success: Bool) in
            group.leave()
        })
    }
    // 網(wǎng)絡(luò)請求2
    group.enter()
    concurrentQ.async {
        DDYRequest.request(["test":"2"], { (success: Bool) in
            group.leave()
        })
    }
    // 調(diào)度組里的任務(wù)都執(zhí)行完畢執(zhí)行
    group.notify(queue: concurrentQ) {
        DispatchQueue.global().async {
            // 處理數(shù)據(jù)
            DispatchQueue.main.async {
                // 刷新UI
            }
        }
    }
    

    場景2:A執(zhí)行完才執(zhí)行B(第一次請求網(wǎng)絡(luò)拿到ID去再次請求網(wǎng)絡(luò)拿具體數(shù)據(jù),最后刷新UI)

    // 并發(fā)隊(duì)列
    let concurrentQ = DispatchQueue(label: "com.ddy.concurrentQueue", attributes: .concurrent)
    // 創(chuàng)建組
    let group = DispatchGroup()
    // 網(wǎng)絡(luò)請求1
    group.enter()
    concurrentQ.async {
        DDYRequest.request(["test":"1"], { (success: Bool) in
            print("離開 1")
            group.leave()
        })
    }
    group.wait()
    // 網(wǎng)絡(luò)請求2
    group.enter()
    concurrentQ.async {
        DDYRequest.request(["test":"2"], { (success: Bool) in
            print("離開 2")
            group.leave()
        })
    }
    // 調(diào)度組里的任務(wù)都執(zhí)行完畢執(zhí)行
    group.notify(queue: concurrentQ) {
        DispatchQueue.global().async {
            print("處理數(shù)據(jù)")
            DispatchQueue.main.async {
                print("刷新UI")
            }
        }
    }
    

    group.wait(timeout: DispatchTime(uptimeNanoseconds: 10*NSEC_PER_SEC)) 來表示等待與超時(shí)

GCD一些常用函數(shù)

  • asyncAfter 延遲添加調(diào)用

    asyncAfter并不是在指定時(shí)間后執(zhí)行任務(wù)處理,而是在指定時(shí)間后把任務(wù)追加到queue里面。因此會有少許延遲。

    DispatchQueue.global().asyncAfter(deadline: DispatchTime.now()+2.0) {
        print("2秒后執(zhí)行的")
    }
    // let delay = DispatchTime.now() + Double(Int64(3 * 1000 * 1000000)) / Double(NSEC_PER_SEC)
    // let delay = DispatchTime.now() + DispatchTimeInterval.seconds(10)
    let delay = DispatchTime.now() + 10
    DispatchQueue.main.asyncAfter(deadline: delay) {
        print("10秒后執(zhí)行的")
    } 
    // 注意:我們不能直接取消我們已經(jīng)提交到 asyncAfter 里的任務(wù)代碼。
    // 如需取消正在等待執(zhí)行的Block操作,可先將這個(gè)Block封裝到DispatchWorkItem對象中,然后對其發(fā)送cancle,來取消一個(gè)正在等待執(zhí)行的block
    // 將要執(zhí)行的操作封裝到DispatchWorkItem中
    let task = DispatchWorkItem { print("3秒后執(zhí)行被取消") }
    // 延時(shí)2秒執(zhí)行
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.0, execute: task)
    // 取消任務(wù)
    task.cancel()
    
  • 循環(huán)執(zhí)行concurrentPerform(OC快速迭代 apply)

    OC中GCD的dispatch_apply(),而Swift中用concurrentPerform()

    let concurrentQ = DispatchQueue(label: "com.ddy.concurrentQueue", attributes:.concurrent)
    let array = ["1", "3", "5", "7", "9"];
    concurrentQ.async {
        DispatchQueue.concurrentPerform(iterations: array.count) { (index) in
            print("\(array[index])")
        }
    }
    // 1 9 3 5 7  可以利用多核的優(yōu)勢,所以無序
    // 簡化 迭代五次
    // DispatchQueue.concurrentPerform(iterations: 5) {
    //      print("\($0)")
    //  }
    
  • 信號量 semaphore

    創(chuàng)建信號量對象,調(diào)用signal方法發(fā)送信號,信號加1,調(diào)用wait方法等待,信號減1.用信號量實(shí)現(xiàn)剛剛的多個(gè)請求功能。

    // DispatchSemaphore(value: ):用于創(chuàng)建信號量,可以指定初始化信號量計(jì)數(shù)值,這里我們默認(rèn)1.
    // semaphore.wait():會判斷信號量。如果是0,則等待,如果非0,則往下執(zhí)行
    // semaphore.signal():代表運(yùn)行結(jié)束,信號量加1,有等待的任務(wù)這個(gè)時(shí)候才會繼續(xù)執(zhí)行。
    let queue = DispatchQueue.global()
    let group = DispatchGroup()
    let semaphore = DispatchSemaphore(value: 0)
    
    queue.async(group: group) {
       DispatchQueue.main.asyncAfter(deadline: .now() + 0.6, execute: {
           semaphore.signal()
           print("Task one finished")
       })
       semaphore.wait()
    }
    queue.async(group: group) {
       DispatchQueue.main.asyncAfter(deadline: .now() + 1.5, 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")
    }
    
  • 柵欄操作 barrier

    GCD里的Barrier和NSOperationQueue的dependency比較接近
    只能用在自定義并行隊(duì)列,且只保證任務(wù)中代碼執(zhí)行到,如果任務(wù)中存在異步則不保證執(zhí)行完

    let concurrentQ = DispatchQueue(label: "com.ddy.barrier", attributes: .concurrent)
    concurrentQ.async {
        print("task 0-0")
        DDYRequest.request(2, { (success: Bool) in
            print("task 0-1")
        })
        print("task 0-3")
    }
    concurrentQ.async {
        print("task 1-0")
        DDYRequest.request(2, { (success: Bool) in
            print("task 1-1")
        })
        print("task 1-3")
    }
    concurrentQ.async(flags: .barrier) {
        print("task 2-0")
        DDYRequest.request(2, { (success: Bool) in
            print("task 2-1")
        })
    }
    concurrentQ.async {
        print("task 3-0")
        DDYRequest.request(2, { (success: Bool) in
            print("task 3-1")
        })
    }
    
    let concurrentQ = DispatchQueue(label: "com.ddy.barrier", attributes: .concurrent)
    concurrentQ.async {
        print("task 0-0")
        DDYRequest.request(2, { (success: Bool) in
            print("task 0-1")
        })
        print("task 0-3")
    }
    concurrentQ.async {
        print("task 1-0")
        DDYRequest.request(2, { (success: Bool) in
            print("task 1-1")
        })
        print("task 1-3")
    }
    let writeTask = DispatchWorkItem(flags: .barrier) {
        print("task 2-0")
        DDYRequest.request(2, { (success: Bool) in
            print("task 2-1")
        })
    }
    concurrentQ.async(execute: writeTask)
    concurrentQ.async {
        print("task 3-0")
        DDYRequest.request(2, { (success: Bool) in
            print("task 3-1")
        })
    }
    

    執(zhí)行順序分析
    先排除各個(gè)任務(wù)中異步延遲操作(該操作已經(jīng)執(zhí)行到,但不保證執(zhí)行完回調(diào))
    0-0
    (0-3 1-0 1-3) or (1-0 1-3 0-3) or (1-0 0-3 1-3)
    2-0
    然后分析異步延遲的回調(diào)
    (0-1 1-1 3-1) or (0-1 3-1 1-1) or (1-1 0-1 3-1) or (1-1 3-1 0-1) or (3-1 0-1 1-1) or (3-1 1-1 0-1)
    2-1

  • DispatchSource實(shí)現(xiàn)GCD定時(shí)器

    Timer的無奈:
    Timer的創(chuàng)建與撤銷必須在同一個(gè)線程操作,在多線程環(huán)境下使用不便.
    使用時(shí)必須保證有一個(gè)活躍的runloop,然而主線程的runloop是默認(rèn)開啟的,子線程的runloop卻是默認(rèn)不開啟的,當(dāng)在子線程中使用Timer的時(shí)候還需要先激活runloop,否則Timer是不會起效的.
    內(nèi)存泄漏問題. 在控制器中使用Timer的時(shí)候需要控制器對Timer進(jìn)行強(qiáng)引用,然而Timer還會對控制器進(jìn)行強(qiáng)引用,造成循環(huán)引用最終控制器無法釋放導(dǎo)致內(nèi)存泄漏.

    private class func testGCDTimer() {
        // 倒計(jì)時(shí)總次數(shù)
        var timeCount = 20
        // 自定義并發(fā)隊(duì)列
        let concurrentQ = DispatchQueue(label: "com.ddy.timer", attributes: .concurrent)
        // 在自定義隊(duì)列的定時(shí)器
        let timer = DispatchSource.makeTimerSource(flags: [], queue: concurrentQ)
        // 設(shè)置立即開始 0.5秒循環(huán)一次
        timer.schedule(deadline: .now(), repeating: 0.5)
        // 觸發(fā)回調(diào)事件
        timer.setEventHandler {
            timeCount = timeCount - 1
            if timeCount <= 0 {
                timer.cancel()
            }
            DispatchQueue.main.async {
                print("主線程更新UI \(timeCount)")
            }
        }
        // cancel事件回調(diào)
        timer.setCancelHandler {
            DispatchQueue.main.async {
                print("已結(jié)束I \(timeCount)")
            }
        }
        // 啟動定時(shí)器
        timer.resume()
    }
    

參考 Swift4 - GCD的使用
參考 Swift4.0 - GCD
參考 多線程之GCD
參考 從使用場景了解GCD新API
iOS_多線程二

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

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

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