什么是GCD
- Grand Central Dispatch 是異步執(zhí)行任務(wù)的技術(shù)之一。開(kāi)發(fā)者只需要定義想要執(zhí)行的任務(wù),并追加到適當(dāng)?shù)腄ispatch Queue中,GCD就能生成必要的線(xiàn)程并執(zhí)行任務(wù)。我們看個(gè)經(jīng)典的例子。
DispatchQueue.global().async {
/*
這里執(zhí)行耗時(shí)操作
*/
var sum = 0
for i in 0...100 {
print("global線(xiàn)程:\(Thread.current)")
sum += i
}
/*
這里刷新UI
*/
DispatchQueue.main.async {
print("main線(xiàn)程:\(Thread.current)")
print(sum)
}
}
- Cocoa 框架提供了NSObject類(lèi)的方法
self.performSelector(inBackground: #selector(doWork), with: nil)
func doWork() {
print("后臺(tái)處理中")
self.performSelector(onMainThread: #selector(finishDone), with: nil, waitUntilDone: true)
}
func finishDone() {
print("后臺(tái)處理完成")
}
多線(xiàn)程編程
我們知道代碼基本上都是從上到下順序執(zhí)行代碼。如果我們現(xiàn)在有個(gè)耗時(shí)的操作,那我們不想程序就卡在這里,我們就需要使用異步去處理,去處理下面的問(wèn)題?;蛘呶覀兺ǔL砑右粋€(gè)progress提示用戶(hù)正在處理某些東西。
隊(duì)列(串行隊(duì)列,并行隊(duì)列)
串行隊(duì)列:?jiǎn)蝹€(gè)線(xiàn)程,等待上個(gè)任務(wù)執(zhí)行完畢,才往后執(zhí)行
并行隊(duì)列:多個(gè)線(xiàn)程,可以同時(shí)執(zhí)行
如其名稱(chēng)所示,是執(zhí)行處理等待的隊(duì)列。
兩個(gè)關(guān)鍵隊(duì)列 main, global
主隊(duì)列,和全局隊(duì)列。我們通常將我們要執(zhí)行的操作,追加到這個(gè)隊(duì)列后。async, sync 來(lái)說(shuō)明這個(gè)block是異步還是同步執(zhí)行。測(cè)試main, 和global
DispatchQueue.main.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
}
DispatchQueue.main.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
}
DispatchQueue.main.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
}
DispatchQueue.main.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
}
-打印結(jié)果(main隊(duì)列只有一個(gè)線(xiàn)程所以是串行隊(duì)列)
- 當(dāng)前線(xiàn)程<NSThread: 0x7b257510>{number = 1, name = main}------->1
- 當(dāng)前線(xiàn)程<NSThread: 0x7b257510>{number = 1, name = main}------->2
- 當(dāng)前線(xiàn)程<NSThread: 0x7b257510>{number = 1, name = main}------->3
- 當(dāng)前線(xiàn)程<NSThread: 0x7b257510>{number = 1, name = main}------->4
- 測(cè)試global隊(duì)列
DispatchQueue.global().async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
}
DispatchQueue.global().async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
}
DispatchQueue.global().async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
}
DispatchQueue.global().async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
}
- 打印結(jié)果(global隊(duì)列多個(gè)線(xiàn)程執(zhí)行,并行執(zhí)行是并行隊(duì)列)
- 當(dāng)前線(xiàn)程<NSThread: 0x7d160c10>{number = 3, name = (null)}------->3
- 當(dāng)前線(xiàn)程<NSThread: 0x7bfa9dd0>{number = 2, name = (null)}------->1
- 當(dāng)前線(xiàn)程<NSThread: 0x7d15de60>{number = 4, name = (null)}------->4
- 當(dāng)前線(xiàn)程<NSThread: 0x7d153b90>{number = 5, name = (null)}------->2
如何創(chuàng)建隊(duì)列
當(dāng)然除了系統(tǒng)提供給我們的這兩個(gè)隊(duì)列,我們還可以自己創(chuàng)建隊(duì)列。
串行隊(duì)列
let queue = DispatchQueue(label: "myQueue")
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
}
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
}
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
}
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
}
// 打印結(jié)果
1. 當(dāng)前線(xiàn)程<NSThread:0x7c0825f0>{number = 2, name = (null)}------->1
2. 當(dāng)前線(xiàn)程<NSThread: 0x7c0825f0>{number = 2, name = (null)}------->2
3. 當(dāng)前線(xiàn)程<NSThread: 0x7c0825f0>{number = 2, name = (null)}------->3
4. 當(dāng)前線(xiàn)程<NSThread: 0x7c0825f0>{number = 2, name = (null)}------->4
- 并行隊(duì)列
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
- qos 隊(duì)列的優(yōu)先等級(jí)
- User Interactive 和用戶(hù)交互相關(guān),比如動(dòng)畫(huà)等等優(yōu)先級(jí)最高。比如用戶(hù)連續(xù)拖拽的計(jì)算
- User Initiated 需要立刻的結(jié)果,比如push一個(gè)ViewController之前的數(shù)據(jù)計(jì)算
- Utility 可以執(zhí)行很長(zhǎng)時(shí)間,再通知用戶(hù)結(jié)果。比如下載一個(gè)文件,給用戶(hù)下載進(jìn)度。
- Background 用戶(hù)不可見(jiàn),比如在后臺(tái)存儲(chǔ)大量數(shù)據(jù)
attributes 是并行的還是串行的枚舉值(concurrent:并行的, initiallyInactive:串行的)
autoreleaseFrequency 隊(duì)列的釋放規(guī)則
切換隊(duì)列
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
}
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
}
//切換隊(duì)列
queue.setTarget(queue: DispatchQueue.main)
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
}
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
}
- 執(zhí)行結(jié)果
- 當(dāng)前線(xiàn)程<NSThread: 0x7bc2d970>{number = 2, name = (null)}------->2
- 當(dāng)前線(xiàn)程<NSThread: 0x7b63fe10>{number = 3, name = (null)}------->1
- 當(dāng)前線(xiàn)程<NSThread: 0x7b6215b0>{number = 1, name = main}------->3
- 當(dāng)前線(xiàn)程<NSThread: 0x7b6215b0>{number = 1, name = main}------->4
- 延時(shí)任務(wù)
DispatchQueue.main.asyncAfter(deadline: .now() + DispatchTimeInterval.seconds(3)) { //不是三秒后執(zhí)行該任務(wù),而是三秒后添加到改隊(duì)列
//執(zhí)行的代碼
}
隊(duì)列組
對(duì)于串行隊(duì)列來(lái)說(shuō)最后一個(gè)加入的,肯定是最后一個(gè)結(jié)束。但是并行隊(duì)列來(lái)說(shuō)我們?cè)趺粗?,這些任務(wù)都完成了吶。
看看串行隊(duì)列的處理方式
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.initiallyInactive, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
}
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
}
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
}
queue.async {
print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
DispatchQueue.main.async {
print("執(zhí)行完了")
}
}
- 打印結(jié)果
- 當(dāng)前線(xiàn)程<NSThread: 0x7a66cea0>{number = 2, name = (null)}------->1
- 當(dāng)前線(xiàn)程<NSThread: 0x7a66cea0>{number = 2, name = (null)}------->2
- 當(dāng)前線(xiàn)程<NSThread: 0x7a66cea0>{number = 2, name = (null)}------->3
- 當(dāng)前線(xiàn)程<NSThread: 0x7a66cea0>{number = 2, name = (null)}------->4
- 執(zhí)行完了
- 并行隊(duì)列我們可以使用group去解決這個(gè)問(wèn)題
let group = DispatchGroup()
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
queue.async(group: group, execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
})
queue.async(group: group, execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
})
queue.async(group: group, execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
})
queue.async(group: group, execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
})
let workItem = DispatchWorkItem {
print("執(zhí)行完成")
} group.notify(queue: queue, work: workItem)
- 打印結(jié)果
- 當(dāng)前線(xiàn)程<NSThread: 0x78e49ae0>{number = 2, name = (null)}------->2
- 當(dāng)前線(xiàn)程<NSThread: 0x790a4710>{number = 3, name = (null)}------->1
- 當(dāng)前線(xiàn)程<NSThread: 0x78e53d00>{number = 5, name = (null)}------->3
- 當(dāng)前線(xiàn)程<NSThread: 0x78e43b60>{number = 4, name = (null)}------->4
- 執(zhí)行完成
- group中wait方法
和notify不同的是wait是等待一段時(shí)間,不管隊(duì)列的任務(wù)是否都完成了,都會(huì)調(diào)用。
- DispatchWorkItem
let workItem = DispatchWorkItem {
print("開(kāi)始干活\(Thread.current)")
}
// workItem.perform() //立即執(zhí)行, 放到了當(dāng)前main隊(duì)列中
// DispatchQueue.global().async(execute: workItem) //放到global隊(duì)列中
- group的enter 和 leave
let group = DispatchGroup()
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
queue.async(group: group, execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
queue.async(execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
})
queue.async(execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
})
})
let workItem = DispatchWorkItem {
print("done")
}
group.notify(queue: queue, work: workItem)
- 打印結(jié)果
- 當(dāng)前線(xiàn)程<NSThread: 0x7b967590>{number = 2, name = (null)}------->1
- done
- 當(dāng)前線(xiàn)程<NSThread: 0x797807d0>{number = 1, name = main}------->2
- 當(dāng)前線(xiàn)程<NSThread: 0x7b8bd8b0>{number = 3, name = (null)}------->3
- 我們?cè)囅氘?dāng)我們的并行隊(duì)列里還有其他的并行隊(duì)列,我們想等他們都操作完之后,在執(zhí)行下一步,該怎么辦。
let group = DispatchGroup()
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
queue.async(group: group, execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
group.enter()
queue.async(execute: {
group.leave()
print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
})
group.enter()
queue.async(execute: {
print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
group.leave()
})
})
let workItem = DispatchWorkItem {
print("done")
}
group.notify(queue: queue, work: workItem)
- 當(dāng)前線(xiàn)程<NSThread: 0x7a95e9e0>{number = 2, name = (null)}------->1
- 當(dāng)前線(xiàn)程<NSThread: 0x7a95e9e0>{number = 2, name = (null)}------->2
- 當(dāng)前線(xiàn)程<NSThread: 0x7a963a70>{number = 3, name = (null)}------->3
- done
- 并行隊(duì)列處理數(shù)據(jù)的時(shí)候出現(xiàn)的問(wèn)題
var array: [Int] = []
for i in 0...100 {
DispatchQueue.global().async(execute: {
array.append(i)
})
}
- 多個(gè)線(xiàn)程去更新array時(shí)就會(huì)出現(xiàn)內(nèi)存錯(cuò)誤。同一個(gè)線(xiàn)程訪(fǎng)問(wèn)了同一塊地址內(nèi)存,同時(shí)往里面進(jìn)行寫(xiě)數(shù)據(jù)。
Demo(61868,0x38bc1c0) malloc: *** error for object 0x7ac34424: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
(lldb)
- 使用信號(hào)量解決
var array: [Int] = []
let semaphore = DispatchSemaphore(value: 1)
for i in 0...100 {
DispatchQueue.global().async(execute: {
/*
wait 等待信號(hào)量semaphore >= 1時(shí)執(zhí)行
wait返回時(shí) semaphore 減1
*/
_ = semaphore.wait(wallTimeout: .distantFuture)
array.append(i) //只會(huì)同時(shí)被一個(gè)線(xiàn)程訪(fǎng)問(wèn)
/*
signal semaphore 加1
*/
semaphore.signal()
})
}
DispatchQueue.main.async {
print(array.count)
}
- 使用互斥鎖
let lock = NSLock()
var array: [Int] = []
for i in 0...100 {
DispatchQueue.global().async(execute: {
lock.lock()
array.append(i)
lock.unlock()
})
}
DispatchQueue.main.async {
print(array.count)
}
死鎖(線(xiàn)程之間互相等待對(duì)方執(zhí)行完之后才能接著執(zhí)行)
- main隊(duì)列里執(zhí)行下面代碼造成死鎖。使用sync編程時(shí)要注意死鎖的發(fā)生。
DispatchQueue.main.sync {
print("死鎖了")
}
DispatchSource 調(diào)度源
它的作用是當(dāng)有一些特定的較底層的系統(tǒng)事件發(fā)生時(shí),調(diào)度源會(huì)捕捉到這些事件,然后可以做其他的邏輯處理,調(diào)度源有多種類(lèi)型,分別監(jiān)聽(tīng)對(duì)應(yīng)類(lèi)型的系統(tǒng)事件。我們來(lái)看看它都有哪些類(lèi)型
- DISPATCH_SOURCE_TYPE_DATA_ADD:屬于自定義事件,可以通過(guò)dispatch_source_get_data函數(shù)獲取事件變量數(shù)據(jù),在我們自定義的方法中可以調(diào)用dispatch_source_merge_data函數(shù)向Dispatch Source設(shè)置數(shù)據(jù),下文中會(huì)有詳細(xì)的演示。
- DISPATCH_SOURCE_TYPE_DATA_OR:屬于自定義事件,用法同上面的類(lèi)型一樣。
- DISPATCH_SOURCE_TYPE_MACH_SEND:Mach端口發(fā)送事件。
- DISPATCH_SOURCE_TYPE_MACH_RECV:Mach端口接收事件。
- DISPATCH_SOURCE_TYPE_PROC:與進(jìn)程相關(guān)的事件。
- DISPATCH_SOURCE_TYPE_READ:讀文件事件。
- DISPATCH_SOURCE_TYPE_WRITE:寫(xiě)文件事件。
- DISPATCH_SOURCE_TYPE_VNODE:文件屬性更改事件。
- DISPATCH_SOURCE_TYPE_SIGNAL:接收信號(hào)事件。
- DISPATCH_SOURCE_TYPE_TIMER:定時(shí)器事件。
- DISPATCH_SOURCE_TYPE_MEMORYPRESSURE:內(nèi)存壓力事件。
- 看例子每隔一秒執(zhí)行操作
private var timer: DispatchSourceTimer!
self.timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags.strict, queue: DispatchQueue.global())
self.timer.scheduleRepeating(deadline: .now(), interval: DispatchTimeInterval.seconds(1)) //每隔一秒執(zhí)行下面的操作
self.timer.setEventHandler {
DispatchQueue.main.async(execute: {
//回到主線(xiàn)程刷新UI
print(Date())
})
}
self.timer.resume()
你可以使用改裝一下做個(gè)倒計(jì)時(shí)。當(dāng)獲取激活碼時(shí)。
假如我們有5個(gè)線(xiàn)程去下載,我們想知道這五個(gè)線(xiàn)程的下載進(jìn)度。
private var dispatchDataSource: DispatchSourceUserDataAdd!
var progress: UInt = 0
self.dispatchDataSource = DispatchSource.makeUserDataAddSource(queue: DispatchQueue.main)
self.dispatchDataSource.setEventHandler {
let data = UInt(self.dispatchDataSource.data) //獲取數(shù)據(jù)中心的data
progress += data
print("上傳任務(wù)進(jìn)度:\(progress)")
}
self.dispatchDataSource.resume()
for _ in 0...5 {
DispatchQueue.global().async(execute: {
print("異步處理任務(wù)")
self.dispatchDataSource.add(data: 1) //將數(shù)組添加數(shù)據(jù)中心
})
}
- 打印結(jié)果
異步處理任務(wù)
異步處理任務(wù)
異步處理任務(wù)
異步處理任務(wù)
異步處理任務(wù)
異步處理任務(wù)
上傳任務(wù)進(jìn)度:6