【iOS印象】 併發編程:Operation Queues


基本概念
  • 進程(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

更多參考

相關文章
相關標籤/搜索