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)程中可以開(kāi)啟多條線程,每條線程可以并行(同時(shí))執(zhí)行不同的任務(wù),進(jìn)程只負(fù)責(zé)資源的調(diào)度和分配,線程才是程序真正的執(zhí)行單元,負(fù)責(zé)代碼的執(zhí)行。

  • 單線程與多線程對(duì)比

  • 單線程程序:只有一個(gè)線程,代碼順序執(zhí)行,容易出現(xiàn)代碼阻塞(頁(yè)面假死)。
  • 多線程程序:有多個(gè)線程,線程間獨(dú)立運(yùn)行,能有效的避免代碼阻塞,并且提高程序的運(yùn)行性能。
  • 線程相關(guān)
  • 同步線程:同步線程會(huì)阻塞當(dāng)前線程去執(zhí)行線程內(nèi)的任務(wù),執(zhí)行完之后才會(huì)反回當(dāng)前線程。
  • 異步線程:異步線程不會(huì)阻塞當(dāng)前線程,會(huì)開(kāi)啟其他線程去執(zhí)行線程內(nèi)的任務(wù)。
  • 串行隊(duì)列:線程任務(wù)按先后順序逐個(gè)執(zhí)行(需要等待隊(duì)列里面前面的任務(wù)執(zhí)行完之后再執(zhí)行新的任務(wù))。
  • 并發(fā)隊(duì)列:多個(gè)任務(wù)按添加順序一起開(kāi)始執(zhí)行(不用等待前面的任務(wù)執(zhí)行完再執(zhí)行新的任務(wù)),但是添加間隔往往忽略不計(jì),所以看著像是一起執(zhí)行的。
  • 并發(fā)VS并行:并行是基于多核設(shè)備的,并行一定是并發(fā),并發(fā)不一定是并行。
  • 死鎖
    死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過(guò)程中,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(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)程使用。
  • 請(qǐng)求與保持條件:一個(gè)進(jìn)程因請(qǐng)求資源而阻塞時(shí),對(duì)已獲得的資源保持不放。
  • 不剝奪條件:進(jìn)程已獲得的資源,在末使用完之前,不能強(qiáng)行剝奪。
  • 循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
  • 避免死鎖的方法
  1. 破壞“互斥”條件:就是在系統(tǒng)里取消互斥。若資源不被一個(gè)進(jìn)程獨(dú)占使用,那么死鎖是肯定不會(huì)發(fā)生的。但一般“互斥”條件是無(wú)法破壞的。因此,在死鎖預(yù)防里主要是破壞其他三個(gè)必要條件,而不去涉及破壞“互斥”條件。
  2. 破壞“請(qǐng)求和保持”條件:在系統(tǒng)中不允許進(jìn)程在已獲得某種資源的情況下,申請(qǐng)其他資源。即要想出一個(gè)辦法,阻止進(jìn)程在持有資源的同時(shí)申請(qǐng)其他資源。
    方法:要求每個(gè)進(jìn)程提出新的資源申請(qǐng)前,釋放它所占有的資源。這樣,一個(gè)進(jìn)程在需要資源S時(shí),須先把它先前占有的資源R釋放掉,然后才能提出對(duì)S的申請(qǐng),即使它可能很快又要用到資源R。
  3. 破壞“不可搶占”條件:允許對(duì)資源實(shí)行搶奪。
    方法一:如果占有某些資源的一個(gè)進(jìn)程進(jìn)行進(jìn)一步資源請(qǐng)求被拒絕,則該進(jìn)程必須釋放它最初占有的資源,如果有必要,可再次請(qǐng)求這些資源和另外的資源。
    方法二:如果一個(gè)進(jìn)程請(qǐng)求當(dāng)前被另一個(gè)進(jìn)程占有的一個(gè)資源,則操作系統(tǒng)可以搶占另一個(gè)進(jìn)程,要求它釋放資源。只有在任意兩個(gè)進(jìn)程的優(yōu)先級(jí)都不相同的條件下,該方法才能預(yù)防死鎖。
  4. 破壞“循環(huán)等待”條件:將系統(tǒng)中的所有資源統(tǒng)一編號(hào),進(jìn)程可在任何時(shí)刻提出資源申請(qǐng),但所有申請(qǐng)必須按照資源的編號(hào)順序(升序)提出。這樣做就能保證系統(tǒng)不出現(xiàn)死鎖。
    方法:利用銀行家算法避免死鎖。

死鎖參考

  • 線程安全

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

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

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

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

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

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

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

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

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

GCD抽象層次最高,使用也簡(jiǎn)單,因此,蘋果也推薦使用GCD

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

GCD

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

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

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

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

    2.主線程串行隊(duì)列異步執(zhí)行任務(wù),在主線程運(yùn)行,不會(huì)產(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)生成,無(wú)法調(diào)用conQueue.resume()和Queue.suspend()來(lái)控制執(zhí)行繼續(xù)或中斷。

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

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

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

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

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

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

    // Swift3開(kāi)始使用了DispatchWorkItem類將任務(wù)封裝成為對(duì)象,由對(duì)象進(jìn)行任務(wù)。
    let item = DispatchWorkItem {
        // do task
    }
    DispatchQueue.global().async(execute: item)
    
    // 也可以使用DispatchWorkItem實(shí)例對(duì)象的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("沒(méi)執(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ù)要小心 可能 會(huì)死鎖

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í)行 不會(huì)死鎖)

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

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

    // 并發(fā)隊(duì)列
    let concurrentQ = DispatchQueue(label: "com.ddy.concurrentQueue", attributes: .concurrent)
    // 創(chuàng)建組
    let group = DispatchGroup()
    // 網(wǎng)絡(luò)請(qǐng)求1
    group.enter()
    concurrentQ.async {
        DDYRequest.request(["test":"1"], { (success: Bool) in
            group.leave()
        })
    }
    // 網(wǎng)絡(luò)請(qǐng)求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
            }
        }
    }
    

    場(chǎng)景2:A執(zhí)行完才執(zhí)行B(第一次請(qǐng)求網(wǎng)絡(luò)拿到ID去再次請(qǐng)求網(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ò)請(qǐng)求1
    group.enter()
    concurrentQ.async {
        DDYRequest.request(["test":"1"], { (success: Bool) in
            print("離開(kāi) 1")
            group.leave()
        })
    }
    group.wait()
    // 網(wǎng)絡(luò)請(qǐng)求2
    group.enter()
    concurrentQ.async {
        DDYRequest.request(["test":"2"], { (success: Bool) in
            print("離開(kāi) 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)) 來(lái)表示等待與超時(shí)

GCD一些常用函數(shù)

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

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

    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對(duì)象中,然后對(duì)其發(fā)送cancle,來(lái)取消一個(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)勢(shì),所以無(wú)序
    // 簡(jiǎn)化 迭代五次
    // DispatchQueue.concurrentPerform(iterations: 5) {
    //      print("\($0)")
    //  }
    
  • 信號(hào)量 semaphore

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

    // DispatchSemaphore(value: ):用于創(chuàng)建信號(hào)量,可以指定初始化信號(hào)量計(jì)數(shù)值,這里我們默認(rèn)1.
    // semaphore.wait():會(huì)判斷信號(hào)量。如果是0,則等待,如果非0,則往下執(zhí)行
    // semaphore.signal():代表運(yùn)行結(jié)束,信號(hào)量加1,有等待的任務(wù)這個(gè)時(shí)候才會(huì)繼續(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的無(wú)奈:
    Timer的創(chuàng)建與撤銷必須在同一個(gè)線程操作,在多線程環(huán)境下使用不便.
    使用時(shí)必須保證有一個(gè)活躍的runloop,然而主線程的runloop是默認(rèn)開(kāi)啟的,子線程的runloop卻是默認(rèn)不開(kāi)啟的,當(dāng)在子線程中使用Timer的時(shí)候還需要先激活runloop,否則Timer是不會(huì)起效的.
    內(nèi)存泄漏問(wèn)題. 在控制器中使用Timer的時(shí)候需要控制器對(duì)Timer進(jìn)行強(qiáng)引用,然而Timer還會(huì)對(duì)控制器進(jìn)行強(qiáng)引用,造成循環(huán)引用最終控制器無(wú)法釋放導(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è)置立即開(kāi)始 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)")
            }
        }
        // 啟動(dòng)定時(shí)器
        timer.resume()
    }
    

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

最后編輯于
?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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