基本概念
- 程式(Process):一個正在執行中的可執行檔案。每一個程式都有獨立的記憶體空間和系統資源(埠許可權等),至少包含一個主執行緒和任意數量的其他(輔)執行緒。當一個程式的主執行緒退出時,則該程式即結束了。
- 執行緒(Thread):一個獨立的程式碼執行路徑,也即執行緒是程式碼執行路徑的最小分支。iOS 中,執行緒底層基於 POSIX threads APIs,即 pthreads。
- 任務(Task):需要執行的工作(一段程式碼),是個抽象概念。
- 序列 vs. 併發:主要區別在於允許同時執行的任務數量
- 序列:一次只能執行一個任務,必須等一個任務執行完成後才執行下一個任務。
- 併發:指允許多個任務同時執行。(note: 其實也是某一時刻只執行一個任務,系統在多個任務之間進行快速切換,區別於並行)
- 同步 vs. 非同步:主要區別在於是否等待任務執行完成,即是否阻塞當前執行緒。
- 同步:會等待執行完成後再繼續執行接下來的程式碼
- 非同步:呼叫後立即返回,不會等待執行操作的執行結果
- 佇列 vs. 執行緒:
- 佇列:iOS 中,分為序列佇列與併發佇列,用於處理不同需要的任務。
- 執行緒:iOS 系統使用佇列進行任務排程,根據任務需要和系統負載情況動態地建立和銷燬執行緒,而無須手動管理。
iOS 的併發程式設計模型
在 iOS 中,蘋果與傳統的基於執行緒不同,而是採用佇列,一般只需要定義好排程的任務,並加入到相應的佇列,系統就會在合適的執行緒執行這些任務,而不需要關心執行緒的建立和銷燬。
以下情景應該直接使用執行緒:
- 用執行緒以外的方式無法實現的特定任務
- 必須實時執行一個任務
- 對在後臺執行的任務有更多的可預測行為
Operation Queues vs. Grand Central Dispatch (GCD)
- Operation Queues: 相對增加了一點開銷,更靈活,可通過給 operation 之間新增依賴關係,開始、暫停、恢復,或取消 operation
- GCD:輕量級,以 FIFO 的順序執行併發任務。使用 GCD 時,我們不關心任務具體的排程情況,而是由系統處理,因此也相對不夠靈活
關於 Operation 物件
用於封裝需要執行的任務,Operation 本身是一個抽象類,使用時必須建立自定義子類或使用系統預定義的子類,NSInvocationOperation 和 BlockOperation
- NSInvocationOperation: 通過一個 object 和 selector 建立一個非併發的(non-concurrent) operation(Swift 不可用,BlockOperation 或 OperationQueue.addOperation(block:) 替代)
- BlockOperation: 可用來併發執行一個或多個 block,只有當一個 BlockOperation 關聯的所有 block 執行完畢,這個 operation 才算執行完畢,類似 dispatch_group
另外,所有 operation 都支援以下特性:
- 支援在 operation 之間建立依賴關係,只有當一個 operation 所依賴的所有 operation 都完成時,這個 operation 才能開始執行;
- 支援一個可選的 completion block,會在主任務執行完成時被呼叫;
- 支援通過 KVO 來觀察 operation 執行狀態的變化;
- 支援設定執行的優先順序,從而影響 operation 之間的相對執行順序;
- 支援取消,可以停止正在執行的 operation
併發 vs. 非併發 Operation
一般都是通過將 operation 新增到 operation queue 的方式來執行 operation,但這並不是必須的,還可以直接呼叫 start 方法來執行,但這種方式不能保證 operation 是非同步執行的。Operation 類的 isConcurrent 方法的返回值標識了相對於呼叫 start 方法的執行緒是否非同步執行,預設情況下,isConcurrent 的返回值為 false,即會阻塞呼叫 start 方法的執行緒。
自定義併發執行的 operation,需要編寫一些額外的程式碼支援非同步執行,如建立新執行緒,呼叫系統的非同步方法或其他方式確保 start 方法在開始執行任務後立即返回。但絕大多數情況下,都是將 operation 新增到 operation queue 的方式來執行,因此沒必要實現併發的 operation。
建立 NSInvocationOperation
Swift 不可用,略。
建立 BlockOperation
let blockOperation = BlockOperation(block: {
print("start excute block1")
sleep(1)
print("finish block1")
})
blockOperation.addExecutionBlock {
print("start excute block2")
sleep(1)
print("finish block2")
}
blockOperation.addExecutionBlock {
print("start excute block3")
sleep(1)
print("finish block3")
}
blockOperation.start()複製程式碼
* 非同步執行,blocks 開始與結束的順序不確定
自定義 Operation 物件
- 非併發 Operation 子類
- 執行 main 方法中的主任務
- 響應取消事件:
- 儘管 operation 是支援取消操作的,但卻並不是立即取消的,而是在你呼叫了 operation 的 cancel 方法之後的下一個 isCancelled 的檢查點取消的。
- 併發 Operation 子類額外配置:
- 重寫 start 方法,並且一定不要呼叫父類的實現
- 重寫 isExecuting 和 isFinished,維護這兩個狀態,並生成響應的 KVO 通知
- 重寫 isConcurrent,返回 true.
注意! 即使一個 operation 是被 cancel 掉了,仍需手動觸發 isFinished 的 KVO 通知。因為 operation 依賴會觀察其 isFinished 值變化。
維護 KVO 通知
Operation 類以下 Key Path 支援 KVO 通知:
- isCancelled
- isConcurrent
- isExecuting
- isFinished
- isReady
- dependencies
- queuePriority
- completionBlock
定製 Operation 物件的行為
Operation 的靈活性就體現在執行行為的可定製
- 配置(單向)依賴關係
- addDependency(op:)
- removeDependency(op:)
- 與 operation queue 無關
- 注意!不要迴圈依賴,否則永遠不會執行
- 在手動執行或新增到 operation queue 之前配置好依賴關係,否則可能會失效
- 修改 Operation 在佇列中的優先順序
- .queuePriority
- 第一要素是 operation 的 isReady 狀態(取決於依賴關係)
- 其次即 operation 在佇列中的優先順序
- 預設優先順序 normal
- 應用於相對的 operation queue
- 修改 Operation 線上程中的優先順序(iOS 8.0 廢棄,新增 qualityOfService 替代)
- .threadPriority
- 預設的 start 方法會修改它的執行緒優先順序
- 隻影響 main 方法執行所線上程
- 自定義併發 operation 子類時在 start 方法中也要根據指定的值修改當前執行緒的優先順序
- 設定 Completion Block
- .completionBlock
- 注意!operation 被取消時,completion block 也會執行
- 無法保證在主執行緒執行
執行 Operation
- 將 operation 新增到一個 operation queue,自動執行
- 建立 OperationQueue 物件例項
- 新增 operation
- addOperation(op: Operation)
- addOperations(ops: [Operation], waitUntilFinished: Bool)
- addOperation(block: )
- operation 新增到 operation queue 了就不要再修改了
- 直接呼叫 start 方法手動執行
- 執行前應該做些防範性判斷
- isReady
- isCancel
取消 Operation
operation 加入 operation queue 後,唯一 dequeue 的辦法就是呼叫 operation 的 cancel 方法
等待 Operation 結束
OperationQueue 的 addOperations(ops: [Operation], waitUntilFinished: Bool) 和 Operation 的 waitUntilFinished 方法用於阻塞當前執行緒,直到相關 operation 執行結束。
注意!避免阻塞主執行緒。
暫停和恢復 Operation Queue
暫停佇列並不能暫停正在執行的 operation,只是暫停排程新的 operation
更多參考