iOS多執行緒之GCD、OperationQueue 對比和實踐記錄
阿新 • • 發佈:2020-08-27
[toc]
# 簡介
在計算的早期,計算機可以執行的最大工作量是由 CPU 的時鐘速度決定的。但是隨著技術的進步和處理器設計的緊湊化,熱量和其他物理約束開始限制處理器的最大時鐘速度。因此,晶片製造商尋找其他方法來提高晶片的總體效能。他們決定的解決方案是增加每個晶片上的處理器核心數量。通過增加核心的數量,一個單獨的晶片可以每秒執行更多的指令,而不用增加 CPU 的速度或改變晶片的大小或熱特性。唯一的問題是如何利用額外的核心。
應用程式使用多核的傳統方法是建立多個執行緒。與依賴執行緒不同,iOS 採用非同步設計方法來解決併發問題。通常,這項工作涉及獲取一個後臺執行緒,在該執行緒上啟動所需的任務,然後在任務完成時向呼叫方傳送通知(通常通過一個回撥函式)。
iOS 提供了一些技術,允許您非同步執行任何任務,而無需自己管理執行緒。非同步啟動任務的技術之一是 Grand Central Dispatch (GCD)。這種技術採用執行緒管理程式碼,並將該程式碼移動到系統級別。您所要做的就是定義要執行的任務,並將它們新增到適當的分派佇列中。GCD 負責建立所需的執行緒,並安排任務在這些執行緒上執行。由於執行緒管理現在是系統的一部分,GCD 提供了任務管理和執行的整體方法,比傳統執行緒提供了更高的效率。
OperationQueue(操作佇列,api 類名為 NSOperationQueue )是 Objective-C 物件,是對 GCD 的封裝。其作用非常類似於分派佇列。您定義要執行的任務,然後將它們新增到 OperationQueue 中, OperationQueue 處理這些任務的排程和執行。與 GCD 一樣, OperationQueue 為您處理所有執行緒管理,確保在系統上儘可能快速有效地執行任務。
# GCD、OperationQueue 對比
## 核心理念
* GCD的核心概念:將 任務(block) 新增到佇列,並且指定執行任務的函式。
* NSOperation 的核心概念:把 操作(非同步) 新增到 佇列。
## 區別
* GCD:
* 將任務(block)新增到佇列(序列/併發/主佇列),並且指定任務執行的函式(同步/非同步)
* GCD是底層的C語言構成的API
* iOS 4.0 推出的,針對多核處理器的併發技術
* 在佇列中執行的是由 block 構成的任務,這是一個輕量級的資料結構
* 要停止已經加入 queue 的 block 需要寫複雜的程式碼
* 需要通過 Barrier(dispatch_barrier_async)或者同步任務設定任務之間的依賴關係
* 只能設定佇列的優先順序
* 高階功能:
dispatch_once_t(一次性執行, 多執行緒安全);
dispatch_after(延遲);
dispatch_group(排程組);
dispatch_semaphore(訊號量);
dispatch_apply(優化順序不敏感大體量for迴圈);
* OperationQueue:
* OC 框架,更加面向物件,是對 GCD 的封裝。
* iOS 2.0 推出的,蘋果推出 GCD 之後,對 NSOperation 的底層進行了全部重寫。
* 可以設定佇列中每一個操作的 QOS() 佇列的整體 QOS
* 操作相關
Operation作為一個物件,為我們提供了更多的選擇:
任務依賴(addDependency),可以跨佇列設定操作的依賴關係;
在佇列中的優先順序(queuePriority)
服務質量(qualityOfService, iOS8+);
完成回撥(void (^completionBlock)(void)
* 佇列相關
服務質量(qualityOfService, iOS8+);
最大併發運算元(maxConcurrentOperationCount),GCD 不易實現;
暫停/繼續(suspended);
取消所有操作(cancelAllOperations);
KVO 監聽佇列任務執行進度(progress, iOS13+);
接下來通過文字,結合實踐程式碼(**工程連結在文末**)和執行效果 gif 圖對部分功能進行分析。
# GCD
## 佇列
### 序列佇列(Serial Queues)
序列佇列中的任務按順序執行;但是不同序列佇列間沒有任何約束; 多個序列佇列同時執行時,不同佇列中任務執行是併發的效果。比如:火車站買票可以有多個賣票口,但是每個排的隊都是序列佇列,整體併發,單線序列。
注意防坑:序列佇列建立的位置。比如下面程式碼示例中:在for迴圈內部建立時,每個迴圈都是建立一個新的序列佇列,裡面只裝一個任務,多個序列佇列,結果整體上是併發的效果。想要序列效果,必須在for迴圈外部建立序列佇列。
序列佇列適合管理共享資源。保證了順序訪問,杜絕了資源競爭。
程式碼示例:
```swift
private func serialExcuteByGCD(){
let lArr : [UIImageView] = [imageView1, imageView2, imageView3, imageView4]
//序列佇列,非同步執行時,只開一個子執行緒
let serialQ = DispatchQueue.init(label: "com.companyName.serial.downImage")
for