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)系。
- 避免死鎖的方法
- 破壞“互斥”條件:就是在系統(tǒng)里取消互斥。若資源不被一個(gè)進(jìn)程獨(dú)占使用,那么死鎖是肯定不會發(fā)生的。但一般“互斥”條件是無法破壞的。因此,在死鎖預(yù)防里主要是破壞其他三個(gè)必要條件,而不去涉及破壞“互斥”條件。
- 破壞“請求和保持”條件:在系統(tǒng)中不允許進(jìn)程在已獲得某種資源的情況下,申請其他資源。即要想出一個(gè)辦法,阻止進(jìn)程在持有資源的同時(shí)申請其他資源。
方法:要求每個(gè)進(jìn)程提出新的資源申請前,釋放它所占有的資源。這樣,一個(gè)進(jìn)程在需要資源S時(shí),須先把它先前占有的資源R釋放掉,然后才能提出對S的申請,即使它可能很快又要用到資源R。 - 破壞“不可搶占”條件:允許對資源實(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ù)防死鎖。 - 破壞“循環(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_多線程二