【翻訳】Parallel Programming with Swift — Part 4/4
この記事は、 Swift による並列プログラミングシリーズのパート4です。パート1では、 Dispatch Queue とシステムが提供するキューについて調べました。パート2では、タスクを定義する別の方法と、 GCD が提供する強力な API に焦点を当てました。パート3では、 Operation と Operation Queue について説明しました。今回は、オペレーションを作成し、オペレーション・キューに追加してみましょう。
このシリーズをすべてご覧になりたい方は、こちらをクリックしてください。
Concurrency & GCD — Parallel Programming with Swift — Part 1/4 (https://medium.com/swift-india/parallel-programming-with-swift-part-1-4-df7caac564ae)
GCD — Parallel Programming with Swift — Part 2/4 (https://medium.com/swift-india/parallel-programming-with-swift-part-2-4-46a3c6262359)
ブロック・オペレーション:
1つまたは複数のブロックの同時実行を管理するオペレーション。 BlockOperation クラスは Operation クラスを継承しています。このオブジェクトを使用すると、複数のブロックを同時に実行することができます。複数のブロックを実行する場合、全てのブロックの実行が終了して初めて Operation 自体が終了したとみなされます。
ブロック・オペレーションに追加されたブロックは、デフォルトの優先順位で適切な作業キューにディスパッチされます。
let queue = OperationQueue()
for i in 1...3 {
let operation = BlockOperation()
operation.addExecutionBlock {
if !operation.isCancelled {
print("###### Operation \(i) in progress ######")
let imageURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imageURL)
OperationQueue.main.addOperation {
print("Image \(i) downloaded...")
}
}
}
operation.queuePriority = .high
queue.addOperation(operation)
}
queue.maxConcurrentOperationCount = 2
queue.waitUntilAllOperationsAreFinished()
queue.cancelAllOperations()
addDependency メソッドを使用することで、指定した operation の完了時に operation を実行することができます。
let queue = OperationQueue()
for i in 1...3 {
let dependentOperation = BlockOperation()
dependentOperation.addExecutionBlock {
if !dependentOperation.isCancelled {
print("###### Operation \(i) in progress ######")
let imageURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imageURL)
print("Image \(i) downloaded...")
}
}
dependentOperation.queuePriority = .high
let operation = BlockOperation {
print("Execute Operation \(i), Once dependent work is done")
}
operation.addDependency(dependentOperation)
queue.addOperation(operation)
queue.addOperation(dependentOperation)
}
queue.maxConcurrentOperationCount = 2
queue.waitUntilAllOperationsAreFinished()
queue.cancelAllOperations()
NSInvocationOperation:
Objective C では NSInvocationOperation を作成できますが、 Swift では利用できません。
非同期オペレーション:
Operation クラスをサブクラス化することで、非同期オペレーションを作成することができます。
// Created by Vasily Ulianov on 09.02.17, updated in 2019.
// License: MIT
import Foundation
/// Subclass of `Operation` that adds support of asynchronous operations.
/// 1. Call `super.main()` when override `main` method.
/// 2. When operation is finished or cancelled set `state = .finished` or `finish()`
open class AsynchronousOperation: Operation {
public override var isAsynchronous: Bool {
return true
}
public override var isExecuting: Bool {
return state == .executing
}
public override var isFinished: Bool {
return state == .finished
}
public override func start() {
if self.isCancelled {
state = .finished
} else {
state = .ready
main()
}
}
open override func main() {
if self.isCancelled {
state = .finished
} else {
state = .executing
}
}
public func finish() {
state = .finished
}
// MARK: - State management
public enum State: String {
case ready = "Ready"
case executing = "Executing"
case finished = "Finished"
fileprivate var keyPath: String { return "is" + self.rawValue }
}
/// Thread-safe computed state value
public var state: State {
get {
stateQueue.sync {
return stateStore
}
}
set {
let oldValue = state
willChangeValue(forKey: state.keyPath)
willChangeValue(forKey: newValue.keyPath)
stateQueue.sync(flags: .barrier) {
stateStore = newValue
}
didChangeValue(forKey: state.keyPath)
didChangeValue(forKey: oldValue.keyPath)
}
}
private let stateQueue = DispatchQueue(label: "AsynchronousOperation State Queue", attributes: .concurrent)
/// Non thread-safe state storage, use only with locks
private var stateStore: State = .ready
}
operation間のデータの受け渡し
operation 間でデータを受け渡すには、さまざまなアプローチがあります。主に採用されているのはアダプター・パターンです。ここでは、新しい BlockOperation を作成してデータを渡します。この記事では、全てのアプローチを分かりやすく紹介します。
Grand Central Dispatch より Operation を使用する利点
依存関係
Operation は、特定の順序でタスクを実行することを可能にする、 operation 間の依存関係を追加する API を提供します。Operationは、すべての依存関係の実行が終了した時点で準備完了となります。
Observable
Operation と OperationQueue には、 KVO を使用して観察できる多くのプロパティがあります。
operation の状態
オペレーションやオペレーション・キューの状態を監視できます。
制御
operation の一時停止、キャンセル、再開ができます。
最大同時動作できるキューの数
同時に実行できるオペレーションキューの最大数を指定できます。maxConcurrentOperationCount に 1 を指定することで、シリアルオペレーションキューを作成できます。
【翻訳元の記事】
Parallel Programming with Swift — Part 4/4
Discussion