一個App是 一個進(jìn)程微饥,一個進(jìn)程擁有 多個線程颅眶。GCD隊列負(fù)責(zé)分配任務(wù)到不同的線程上都哭。
多線程的優(yōu)點:
- 運行更快. 多線程可以并發(fā)處理任務(wù)啊易,速度比串行更快
- 響應(yīng). 如果全部任務(wù)都在主線程進(jìn)行吁伺,那么在出現(xiàn)比較耗時的任務(wù)的時候,用戶會明顯感覺到卡頓造成較差的用戶體驗租谈。
- 優(yōu)化的資源消耗. 多線程經(jīng)過操作系統(tǒng)高度優(yōu)化的篮奄,使得線程切換等消耗極小。
1. 基礎(chǔ)應(yīng)用
串行
let label = "wang.junshuo.example"
let queue = DispatchQueue(label: label)
并發(fā)
let label = "wang.junshuo.example"
let queue = DispatchQueue(label: label, attributes: .concurrent)
在推薦的規(guī)范中垦垂,label
的命名采用BundleID似的反域名的方式加上功能名稱,比如com.company.app
主隊列
應(yīng)用啟動的時候會自動創(chuàng)建負(fù)責(zé)用戶界面的 串行 主隊列宦搬。
由于使用頻率很高,它可以方便的通過DispatchQueue.main
類變量訪問劫拗。
2. 優(yōu)先級(Quality of service)
不同重要程度的任務(wù)通過QoS可以使用不同程度的系統(tǒng)資源和電量间校,達(dá)到高效完成
比如取一個.userInteractive
級別的全局隊列
let queue = DispatchQueue.global(qos: .userInteractive)
級別
系統(tǒng)預(yù)設(shè)的優(yōu)先級有六個。
a. .userInteractive
這個級別的任務(wù)設(shè)計為處理直接與用戶交互相關(guān)的任務(wù)页慷。比如界面更新計算憔足,動畫等。
如果這個任務(wù)不馬上完成酒繁,用戶界面看起來就會卡住滓彰。所以提交到這個隊列任務(wù)需要立即完成.
b. .userInitiated
這個級別的任務(wù)設(shè)計為處理需要立即發(fā)生但是可以異步完成。比如從用戶點擊查看一個文檔州袒,或者從本地數(shù)據(jù)庫讀取信息等揭绑。
這里的任務(wù)也期望短時間或幾秒內(nèi)迅速完成。
c. .default
這個選項是默認(rèn)值郎哭,是作為沒有明確設(shè)定優(yōu)先級的時候的缺省值他匪。最好給每個任務(wù)都設(shè)定一個級別而不是使用這個值。
c. .utility
這個級別的任務(wù)設(shè)計為用于長時間運行的計算等夸研。比如聯(lián)網(wǎng)或連續(xù)數(shù)據(jù)饋送邦蜜。
任務(wù)會在響應(yīng)速度和性能與能源效率之間取得平衡。這里的任務(wù)可能需要幾秒鐘到幾分鐘亥至。
d. .background
這個級別的任務(wù)設(shè)計為用于花費長時間并且用戶不在乎它什么時候完成的悼沈。比如數(shù)據(jù)庫維護(hù),同步遠(yuǎn)程服務(wù)器和備份姐扮。
這里的任務(wù)將專注于能源效率而不是速度絮供,那些用大量時間(幾分鐘或更長)的工作應(yīng)該放這里。
e. .unspecified
這個選項是用于兼容舊版API茶敏,因為有些舊版API可能會使線程超出QoS范圍杯缺。所以不要使用這個值。
優(yōu)先級推斷
如果加入隊列的任務(wù)優(yōu)先級比隊列高睡榆,那么隊列中以及其包含的所有任務(wù)的優(yōu)先級也會自動提升到這個優(yōu)先級萍肆。
3. 添加任務(wù)
比如添加一個網(wǎng)路請求任務(wù)然后刷新
DispatchQueue.global(qos: .utility).async { [weak self] in
guard let self = self else { return }
// 網(wǎng)絡(luò)請求代碼
// 切換到主線程刷新任務(wù)
DispatchQueue.main.async {
self.textLabel.text = "JuHub又更新了哇!"
}
}
注意點
上述代碼有兩點值得注意
a. Weak Self
GCD是不會造成循環(huán)引用的胀屿, 但是在GCD的閉包中強(qiáng)引用self
會使得self
更晚被釋放(直到網(wǎng)絡(luò)請求完成才會釋放)塘揣。
比如在一個將要被dismiss
的viewController
中網(wǎng)絡(luò)請求還沒有完成,則弱引用self
時viewController
會被直接釋放(如果沒有其它循環(huán)引用)宿崭,而強(qiáng)引用的viewController
直到網(wǎng)絡(luò)請求完成才會釋放亲铡。
具體要不要弱引用self
完全取決于業(yè)務(wù)需求
b. 主線程刷新
通常我們都是在網(wǎng)絡(luò)請求完之后異步刷新主線程。使用sync
要非常注意葡兑,因為一旦錯誤的調(diào)用比如在當(dāng)前線程同步任務(wù)中訪問同步隊列中的資源奖蔓,那么當(dāng)前線程就會造成死鎖問題。
在主線程添加同步任務(wù)會造成主線程死鎖
更進(jìn)一步的讹堤,在上一篇中我們講過無法確定隊列中的任務(wù)最終會被分配到哪個線程吆鹤,所以在你創(chuàng)建了一個自定義隊列然后添加同步任務(wù)的時候,這個同步任務(wù)可能還是在主線程執(zhí)行洲守。而此時如果在這個自定義隊列里DispatchQueue.main.sync(){}
就會造成死鎖
func test() {
let queue = DispatchQueue(label: "wang.junshuo.example.testQueue")
print("1\(Thread.current)") // Main Thread
queue.sync {
print("2\(Thread.current)") // Main Thread
DispatchQueue.main.sync {
print("3\(Thread.current)") // Main Thread. 死鎖
}
}
}
所以執(zhí)行同步任務(wù)的時候要十二分的謹(jǐn)慎疑务。
系列文章鏈接
- Swift多線程開發(fā) - 1. 概述
- Swift多線程開發(fā) - 2. GCD 隊列和線程
- Swift多線程開發(fā) - 3. DispatchGroup和Semaphore
- Swift多線程開發(fā) - 4. 多線程開發(fā)的問題
- Swift多線程開發(fā) - 5. Operations
- Swift多線程開發(fā) - 6. Operation 隊列
- Swift多線程開發(fā) - 7. Operation 異步隊列
- Swift多線程開發(fā) - 8. Operation依賴
- Swift多線程開發(fā) - 9. 取消Operation
- Swift多線程開發(fā) - 10. Thread Sanitizer