這時候我們就可以用DispatchGroup
來處理
DispatchGroup
基礎使用,追蹤不同隊列中的任務。
不同隊列可以用來分別處理不同優先級的任務
let group = DispatchGroup()
someQueue.async(group: group) { ... 一些任務 ... }
someQueue.async(group: group) { ... 一些任務 .... }
someOtherQueue.async(group: group) { ... 其它任務 ... }
// 任務全部完成之后會在指定的隊列執行
group.notify(queue: DispatchQueue.main) { [weak self] in
guard let self = self else { return }
self.textLabel.text = "全部任務完成"
}
wait
let group = DispatchGroup()
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async(group: group) {
print("開始任務1")
Thread.sleep(until: Date().addingTimeInterval(4)) // a
print("結束任務1")
}
queue.async(group: group) {
print("開始任務2")
Thread.sleep(until: Date().addingTimeInterval(2)) // b
print("結束任務2")
}
if group.wait(timeout: .now() + 5) == .timedOut { // c
print("啊!我等不下去了!??")
} else {
print("所有任務都已經完成??")
}
上述代碼會輸出
開始任務1
開始任務2
結束任務2
結束任務1
所有任務都已經完成??
如果你把注釋a
的4秒改成6秒,則會輸出
開始任務1
開始任務2
結束任務2
啊!我等不下去了!??
結束任務1
注意輸出的結束任務1
, wait
只是設置了一個閾值來監測任務會不會在規定的時間內完成,并不會停止還未完成的任務
注意,
wait
會阻塞當前線程,所以千萬不要在主線程調用
enter()
和leave()
Dispatch隊列會在閉包內的代碼執行完的時候自動通知DispatchGroup.
但是如果你在Dispatch閉包里調用了一個異步請求,問題就來了,Dispatch內的代碼很有可能在異步請求完成之前就跑完了,然后就告訴Group"這里的任務已經完成了", 但實際并不符合我們的期望。
這時候,我們就可以用group.enter()
和group.leave()
來控制。
這對命令其實就相當于是在計數,當你enter()
的時候,運行中的任務計數+1; 當你leave()
的時候,運行中的任務-1。
queue.dispatch(group: group) {
// count = 1
group.enter()
// count = 2
someAsyncMethod {
// 知行業務邏輯
group.leave() // count -= 1
}
// count -= 1
}
Semaphore
當需要對每個資源精確地限制訪問量的時候就可以用Semaphore
(信號量)了
假設現在有個批量下載任務,你最多允許3個下載任務同時進行
先創建一個只最多允許三個訪問的Semaphore
let semaphore = DispatchSemaphore(value: 3)
模擬10個下載任務
for i in 1...10 {
queue.async(group: group) {
semaphore.wait() // Semaphore計數+1
print("正在下載第\(i)張圖")
// 模擬網絡等待
Thread.sleep(forTimeInterval: 2)
print("第\(i)張圖已下載")
semaphore.signal() // Semaphore計數-1
}
}
上述代碼你會一下子看到
“正在下第1張圖”
“正在下第2張圖”
“正在下第3張圖”
然后直到2秒后,下載任務才會繼續執行
小結
從代碼實現的角度,對于某些業務邏輯,比如下載10張圖片完成后刷新界面,上述兩種方法都可以達到,但是從Dispatch的功能設計出發,優雅的使用方法是
-
DispatchGroup
用于跟蹤任務完成狀態,然后在批量任務完成/未完成的時候來處理業務邏輯 -
DispatchSemorephore
用于控制訪問數量
系列文章鏈接