關於這篇文章的Demo能夠去個人github中MultiThreadDemo查看源碼,若有不當之處,但願你們指出。git
這裏是個人上一篇關於多線程的知識點記錄 iOS 多線程記錄(一)。程序員
GCD是蘋果開發的多線程編程的解決方案,經過簡單的API就能夠實現建立新線程去執行咱們須要執行的任務,不須要咱們手動地建立和管理線程,只須要建立隊列和相應的函數配合使用就行。github
這裏的隊列指執行任務的等待隊列,即用來存聽任務的隊列。隊列是一種特殊的線性表,採用 FIFO(先進先出)的原則,即新任務老是被插入到隊列的末尾,而讀取任務的時候老是從隊列的頭部開始讀取。每讀取一個任務,則從隊列中釋放一個任務。編程
在 GCD 中有兩種隊列:串行隊列和併發隊列。二者的主要區別是:執行順序不一樣,以及開啓線程數不一樣。bash
串行隊列網絡
併發隊列多線程
併發隊列 的併發功能只有在異步函數下才有效。閉包
就是咱們須要執行的操做。執行任務有兩種方式:同步執行(sync)和異步執行(async)。二者的主要區別是:是否等待隊列的任務執行結束,以及是否具有開啓新線程的能力。併發
// 獲取主隊列
let mainQueue = DispatchQueue.main
複製代碼
// 獲取全局隊列
let globalQueue = DispatchQueue.global()
複製代碼
指定隊列的名稱,其它爲默認項,這樣的初始化的列隊有着默認的配置項,默認的列隊是串行列隊。app
let queue = DispatchQueue(label: "com.jiangT.queue")
複製代碼
let queue = DispatchQueue.init(label: "com.jiangT.queue",
qos: DispatchQoS.default,
attributes:DispatchQueue.Attributes.concurrent,
autoreleaseFrequency:DispatchQueue.AutoreleaseFrequency.inherit,
target: nil)
複製代碼
參數介紹:
優先級由最低的background到最高的userInteractive共五個,還有一個爲定義的unspecified.
background:最低優先級,等同於DISPATCH_QUEUE_PRIORITY_BACKGROUND.用戶不可見,好比:在後臺存儲大量數據
utility:優先級等同於DISPATCH_QUEUE_PRIORITY_LOW,能夠執行很長時間,再通知用戶結果。好比:下載一個大文件,網絡,計算
default:默認優先級,優先級等同於DISPATCH_QUEUE_PRIORITY_DEFAULT,建議大多數狀況下使用默認優先級
userInitiated:優先級等同於DISPATCH_QUEUE_PRIORITY_HIGH,須要馬上的結果
.userInteractive:用戶交互相關,爲了好的用戶體驗,任務須要立馬執行。使用該優先級用於UI更新,事件處理和小工做量任務,在主線程執行。
Qos指定了列隊工做的優先級,系統會根據優先級來調度工做,越高的優先級可以越快被執行,可是也會消耗功能,因此準確的指定優先級可以保證app有效的使用資源。
let globalQueue = DispatchQueue.global()
globalQueue.sync {
print("sync + \(Thread.current)")
}
複製代碼
let globalQueue = DispatchQueue.global()
globalQueue.async {
print("async + \(Thread.current)")
}
複製代碼
雖然使用 GCD 只需兩步,可是既然咱們有兩種隊列(串行隊列/併發隊列),兩種任務執行方式(同步執行/異步執行),那麼咱們就有了四種不一樣的組合方式。再加上兩種特殊隊列:全局併發隊列、主隊列。全局併發隊列能夠做爲普通併發隊列來使用。可是主隊列由於有點特殊,因此咱們就又多了兩種組合方式。這樣就有六種不一樣的組合方式了。
- 併發隊列 + 同步執行
- 併發隊列 + 異步執行
- 串行隊列 + 同步執行
- 串行隊列 + 異步執行
- 主隊列 + 同步執行
- 主隊列 + 異步執行
各類組合的結果
區別 | 併發隊列 | 串行隊列 | 主隊列 |
---|---|---|---|
同步(sync) | 沒有開啓新線程,串行執行任務 | 沒有開啓新線程,串行執行任務 | 會形成死鎖 |
異步(async) | 有開啓新線程,併發執行任務 | 有開啓新線程(1條),串行執行任務 | 沒有開啓新線程,串行執行任務 |
DispatchQueue.global().sync {
print("sync1 + \(Thread.current)")
}
DispatchQueue.global().sync {
print("sync2 + \(Thread.current)")
}
-----輸出結果:-----
sync1 + <NSThread: 0x600003bc35c0>{number = 1, name = main}
sync2 + <NSThread: 0x600003bc35c0>{number = 1, name = main}
複製代碼
結論:
DispatchQueue.global().async {
print("begin")
print("async1 + \(Thread.current)")
}
DispatchQueue.global().async {
print("begin")
print("async2 + \(Thread.current)")
}
DispatchQueue.global().async {
print("begin")
print("async3 + \(Thread.current)")
}
DispatchQueue.global().async {
print("begin")
print("async4 + \(Thread.current)")
}
DispatchQueue.global().async {
print("begin")
print("async5 + \(Thread.current)")
}
-----輸出結果:-----
begin
begin
begin
async1 + <NSThread: 0x60000018e540>{number = 3, name = (null)}
begin
async2 + <NSThread: 0x600000196600>{number = 4, name = (null)}
begin
async3 + <NSThread: 0x6000001bc900>{number = 5, name = (null)}
async5 + <NSThread: 0x600000196600>{number = 4, name = (null)}
async4 + <NSThread: 0x60000018e540>{number = 3, name = (null)}
複製代碼
結論:
let queue = DispatchQueue.init(label: "")
queue.sync {
print("sync1 + \(Thread.current)")
}
queue.sync {
print("sync1 + \(Thread.current)")
}
-----輸出結果:-----
sync1 + <NSThread: 0x600000a968c0>{number = 1, name = main}
sync2 + <NSThread: 0x600000a968c0>{number = 1, name = main}
複製代碼
結論:
let queue = DispatchQueue.init(label: "")
queue.async {
print("begin")
print("async1 + \(Thread.current)")
}
queue.async {
print("begin")
print("async2 + \(Thread.current)")
}
queue.async {
print("begin")
print("async3 + \(Thread.current)")
}
-----輸出結果:-----
begin
async1 + <NSThread: 0x60000032b940>{number = 3, name = (null)}
begin
async2 + <NSThread: 0x60000032b940>{number = 3, name = (null)}
begin
async3 + <NSThread: 0x60000032b940>{number = 3, name = (null)}
複製代碼
結論:
- 主隊列:GCD自帶的一種特殊的串行隊列
// 主線程執行同步任務
func syncMain() {
print("方法開始") // 調用方法後,能夠看到這個輸出
DispatchQueue.main.sync {
print("會形成死鎖")
}
}
-----輸出結果:-----
方法開始
複製代碼
結論:
DispatchQueue.main.async {
print("begin")
print("async1 + \(Thread.current)")
}
DispatchQueue.main.async {
print("begin")
print("async2 + \(Thread.current)")
}
DispatchQueue.main.async {
print("begin")
print("async3 + \(Thread.current)")
}
DispatchQueue.main.async {
print("begin")
print("async4 + \(Thread.current)")
}
DispatchQueue.main.async {
print("begin")
print("async5 + \(Thread.current)")
}
-----輸出結果:-----
begin
async1 + <NSThread: 0x600001af6880>{number = 1, name = main}
begin
async2 + <NSThread: 0x600001af6880>{number = 1, name = main}
begin
async3 + <NSThread: 0x600001af6880>{number = 1, name = main}
begin
async4 + <NSThread: 0x600001af6880>{number = 1, name = main}
begin
async5 + <NSThread: 0x600001af6880>{number = 1, name = main}
複製代碼
結論:
/**
* 線程間通訊
*/
DispatchQueue.global().async {
print("async-begin")
print("global + \(Thread.current)")
print("async-end")
let testNum = 666
// 回到主線程
DispatchQueue.main.async {
print("main + \(Thread.current)")
print("pass testNum: \(testNum)")
}
}
-----輸出結果:-----
async-begin
global + <NSThread: 0x60000383d100>{number = 3, name = (null)}
async-end
main + <NSThread: 0x600003821680>{number = 1, name = main}
pass testNum: 666
複製代碼
結論:
DispatchWorkItem是用於幫助DispatchQueue來執行列隊中的任務。
通常狀況下,咱們開啓一個異步線程,會這樣建立列隊並執行async方法,以閉包的方式提交任務。
DispatchQueue.global().async {
// do async task
}
複製代碼
使用了DispatchWorkItem類將任務封裝成爲對象,由對象進行任務。
let item = DispatchWorkItem {
// do task
}
DispatchQueue.global().async(execute: item)
複製代碼
也可使用DispatchWorkItem實例對象的perform方法執行任務
let workItem = DispatchWorkItem {
// do task
}
DispatchQueue.global().async {
workItem.perform()
}
複製代碼
咱們有時須要異步執行兩組操做,並且第一組操做執行完以後,才能開始執行第二組操做。這樣咱們就須要一個至關於 柵欄 同樣的一個方法將兩組異步執行的操做組給分割起來,固然這裏的操做組裏能夠包含一個或多個任務。
柵欄 會等待前邊追加到併發隊列中的任務所有執行完畢以後,再將指定的任務追加到該異步隊列中。
let dataQueue = DispatchQueue(label: "com.jiangT.queue", attributes: .concurrent)
let item = DispatchWorkItem(qos: .default, flags: .barrier) {
print("barrier + \(Thread.current)")
dataQueue.async {
print("async1 + \(Thread.current)")
}
dataQueue.async {
print("async2 + \(Thread.current)")
}
}
dataQueue.async(execute: item)
dataQueue.async {
print("async3 + \(Thread.current)")
}
dataQueue.async {
print("async4 + \(Thread.current)")
}
-----輸出結果:-----
barrier + <NSThread: 0x600000f9d800>{number = 3, name = (null)}
async3 + <NSThread: 0x600000f9da40>{number = 6, name = (null)}
async4 + <NSThread: 0x600000f9bac0>{number = 7, name = (null)}
async1 + <NSThread: 0x600000f9d800>{number = 3, name = (null)}
async2 + <NSThread: 0x600000fa4040>{number = 8, name = (null)}
複製代碼
let workItem = DispatchWorkItem {
// do async task
print(Thread.current)
}
DispatchQueue.global().async {
workItem.perform()
}
workItem.notify(queue: DispatchQueue.main) {
// update UI
print(Thread.current)
}
-----輸出結果:-----
<NSThread: 0x60000193ce40>{number = 3, name = (null)}
<NSThread: 0x600001921100>{number = 1, name = main}
複製代碼
let queue = DispatchQueue(label: "queue", attributes: .concurrent)
let workItem = DispatchWorkItem {
sleep(5)
print("done")
}
queue.async(execute: workItem)
print("before waiting")
workItem.wait()
print("after waiting")
-----輸出結果:-----
before waiting
done
after waiting
複製代碼
let delay = DispatchTime.now() + DispatchTimeInterval.seconds(10)
DispatchQueue.main.asyncAfter(deadline: delay) {
// 延遲執行
}
能夠簡化爲:
let delay = DispatchTime.now() + 10
DispatchQueue.main.asyncAfter(deadline: delay) {
// 延遲執行
}
複製代碼
其它的延時執行方法:
func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem)
func asyncAfter(deadline: DispatchTime, qos: DispatchQoS, flags: DispatchWorkItemFlags, execute: () -> Void)
func asyncAfter(wallDeadline: DispatchWallTime, execute: DispatchWorkItem)
func asyncAfter(wallDeadline: DispatchWallTime, qos: DispatchQoS, flags: DispatchWorkItemFlags, execute: () -> Void)
複製代碼
以前使用GCD的dispatch_apply()執行屢次任務,如今是調用concurrentPerform(),下面是併發執行5次
DispatchQueue.concurrentPerform(iterations: 5) {
print("\($0)")
}
-----輸出結果:-----
2
0
1
3
4
複製代碼
有時候咱們會有這樣的需求:分別異步執行2個耗時任務,而後當2個耗時任務都執行完畢後再回到主線程執行任務。這時候咱們能夠用到 GCD 的隊列組。
let queue = DispatchQueue.global()
let group = DispatchGroup()
group.enter()
queue.async(group: group) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
print("Task one finished")
group.leave()
})
}
group.enter()
queue.async(group: group) {
print("Task two finished")
group.leave()
}
group.enter()
queue.async(group: group) {
print("Task three finished")
group.leave()
}
group.notify(queue: queue) {
print("All task has finished")
}
----輸出結果:-----
Task two finished
Task three finished
Task one finished
All task has finished
複製代碼