Swift Language
並行性
サーチ…
構文
スウィフト3.0
DispatchQueue.main //メインキューを取得する
DispatchQueue(ラベル: "my-serial-queue"、属性:[.serial、.qosBackground])//独自のプライベートシリアルキューを作成する
DispatchQueue.global(attributes:[.qosDefault])//グローバル並行キューにアクセスする
DispatchQueue.main.async {...} //タスクをメインスレッドに非同期にディスパッチします
DispatchQueue.main.sync {...} //メインスレッドに同期してタスクをディスパッチします
DispatchQueue.main.asyncAfter(期限:.now()+ 3){...} // x秒後に実行されるメインスレッドに非同期にタスクをディスパッチする
スイフト<3.0
dispatch_get_main_queue()//メインスレッドで実行中のメインキューを取得する
dispatch_get_global_queue(dispatch_queue_priority_t、0)//指定された優先度を持つグローバルキューを取得するdispatch_queue_priority_t
dispatch_async(dispatch_queue_t){() - > Void in ...} //指定されたdispatch_queue_tでタスクを非同期にディスパッチします
dispatch_sync(dispatch_queue_t){() - > Void in ...} //指定されたdispatch_queue_tでタスクを同期的にディスパッチします
dispatch_after(dispatch_time(DISPATCH_TIME_NOW、Int64(ナノ秒))、dispatch_queue_t、{...}); //ナノ秒後に指定されたdispatch_queue_tでタスクをディスパッチします。
Grand Central Dispatch(GCD)キューの取得
グランドセントラルディスパッチは、「ディスパッチキュー」の概念に基づいて動作します。ディスパッチ・キューは、指定したタスクを渡された順序で実行します。ディスパッチキューには3つのタイプがあります。
- シリアルディスパッチキュー (別名プライベートディスパッチキュー)は、一度に1つのタスクを順に実行します。リソースへのアクセスを同期させるために頻繁に使用されます。
- 並行ディスパッチキュー (別名グローバルディスパッチキュー)は、1つ以上のタスクを同時に実行します。
- メインディスパッチキューは、メインスレッド上でタスクを実行します。
メインキューにアクセスするには:
let mainQueue = DispatchQueue.main
let mainQueue = dispatch_get_main_queue()
システムは、優先度を変えて、グローバルなディスパッチ・キュー(アプリケーションにグローバル)を同時に提供します。 Swift 3のDispatchQueue
クラスを使用して、これらのキューにアクセスできます。
let globalConcurrentQueue = DispatchQueue.global(qos: .default)
に相当
let globalConcurrentQueue = DispatchQueue.global()
let globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
iOS 8以降では、渡す可能性のあるサービス品質値は、 .userInteractive
、 .userInitiated
、 .default
、 .utility
、および.background
です。これらは、 DISPATCH_QUEUE_PRIORITY_
定数を置き換えます。
優先順位を変更して独自のキューを作成することもできます。
let myConcurrentQueue = DispatchQueue(label: "my-concurrent-queue", qos: .userInitiated, attributes: [.concurrent], autoreleaseFrequency: .workItem, target: nil)
let mySerialQueue = DispatchQueue(label: "my-serial-queue", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil)
let myConcurrentQueue = dispatch_queue_create("my-concurrent-queue", DISPATCH_QUEUE_CONCURRENT)
let mySerialQueue = dispatch_queue_create("my-serial-queue", DISPATCH_QUEUE_SERIAL)
Swift 3では、このイニシャライザを使用して作成されたキューは、デフォルトではシリアルに設定され、autoreleaseの頻度に.workItem
を渡すことで、自動解放プールが作成され、各作業項目に対して排水されます。そこにもある.never
あなた自身の自動解放プールを管理することになる自分自身を意味し、または.inherit
環境から設定を継承します。ほとんどの場合、極端なカスタマイズを除いて、おそらく.never
を使用しません。
Grand Central Dispatch(GCD)キューでタスクを実行する
ディスパッチキューでタスクを実行するには、 sync
、 async
、およびafter
メソッドを使用します。
タスクを非同期でキューにディスパッチするには:
let queue = DispatchQueue(label: "myQueueName")
queue.async {
//do something
DispatchQueue.main.async {
//this will be called in main thread
//any UI updates should be placed here
}
}
// ... code here will execute immediately, before the task finished
タスクを同期してキューにディスパッチするには:
queue.sync {
// Do some task
}
// ... code here will not execute until the task is finished
特定の秒数の後にタスクをキューにディスパッチするには:
queue.asyncAfter(deadline: .now() + 3) {
//this will be executed in a background-thread after 3 seconds
}
// ... code here will execute immediately, before the task finished
注:ユーザーインターフェイスの更新は、メインスレッドで呼び出す必要があります!
DispatchQueue.main.async { ... }
中にUIアップデート用のコードを記述していることを確認してくださいDispatchQueue.main.async { ... }
キューのタイプ:
let mainQueue = dispatch_get_main_queue()
let highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
let backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
タスクを非同期でキューにディスパッチするには:
dispatch_async(queue) {
// Your code run run asynchronously. Code is queued and executed
// at some point in the future.
}
// Code after the async block will execute immediately
タスクを同期してキューにディスパッチするには:
dispatch_sync(queue) {
// Your sync code
}
// Code after the sync block will wait until the sync task finished
ある時間間隔後にタスクをディスパッチするには(秒をナノ秒に変換するためにNSEC_PER_SEC
を使用します):
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
// Code to be performed in 2.5 seconds here
}
タスクを非同期で実行し、UIを更新するには:
dispatch_async(queue) {
// Your time consuming code here
dispatch_async(dispatch_get_main_queue()) {
// Update the UI code
}
}
注:ユーザーインターフェイスの更新は、メインスレッドで呼び出す必要があります! UIアップデートのコードを
dispatch_async(dispatch_get_main_queue()) { ... }
中に入れて、
並行ループ
GCDは、ループを実行するためのメカニズムを提供します。これにより、ループは互いに対して同時に発生します。これは、一連の計算コストの高い計算を実行する場合に非常に便利です。
このループを考えてみましょう。
for index in 0 ..< iterations {
// Do something computationally expensive here
}
concurrentPerform
(Swift 3)またはdispatch_apply
(Swift 2)を使用concurrentPerform
、これらの計算を同時に実行できます。
DispatchQueue.concurrentPerform(iterations: iterations) { index in
// Do something computationally expensive here
}
dispatch_apply(iterations, queue) { index in
// Do something computationally expensive here
}
ループ閉包は、 0
からiterations
を含まない各index
に対して呼び出されiterations
。これらの反復は、互いに並行して実行されるため、実行される順序は保証されません。任意の時点で同時に発生する実際の反復回数は、一般に、対象のデバイスの能力(例えば、デバイスのコア数)によって決まります。
特別な考慮事項のいくつか:
concurrentPerform
/dispatch_apply
はループを互いに対して同時に実行するかもしれませんが、これはすべてあなたが呼び出すスレッドに関して同期的に起こります。したがって、ループが完了するまで、スレッドをブロックするので、メインスレッドからこれを呼び出さないでください。これらのループは互いに並行して実行されるため、結果のスレッド安全性を保証する責任があります。たとえば、計算コストの高い計算結果を使用して辞書を更新する場合は、それらの更新を自分で同期させてください。
並行ループの実行にはいくつかのオーバーヘッドが関係していることに注意してください。したがって、ループ内で実行される計算の計算量が十分でない場合、並行ループを使用して得られたすべてのパフォーマンスは、完全に相殺されないと、これらの同時スレッドの同期に関連するオーバーヘッドによって減少する可能性があります。
したがって、ループの各反復で実行される正しい作業量を決定するのは、担当者の責任です。計算が単純すぎる場合は、ループごとにもっと多くの作業を含めるために "ストライド"を使用することができます。たとえば、100万回の簡単な計算で並行ループを実行するのではなく、ループごとに100回の繰り返しを実行し、ループごとに10,000回の計算を実行できます。そうすれば、各スレッドで十分な作業が実行されるため、これらの並行ループの管理に伴うオーバーヘッドはそれほど重要ではなくなります。
OperationQueueでのタスクの実行
OperationQueue
は、実行待ちのタスク行と考えることができます。 GCDのディスパッチキューとは異なり、操作キューはFIFO(先入れ先出し)ではありません。代わりに、実行できるようになるとすぐに、タスクを実行するために十分なシステムリソースがある限り、タスクを実行します。
メインのOperationQueue
取得します。
let mainQueue = OperationQueue.main
カスタムOperationQueue
作成します。
let queue = OperationQueue()
queue.name = "My Queue"
queue.qualityOfService = .default
Quality of Serviceは、作業の重要性、またはタスクからの即時の結果をユーザーがどれだけ評価するかを指定します。
追加Operation
するOperationQueue
:
// An instance of some Operation subclass
let operation = BlockOperation {
// perform task here
}
queue.addOperation(operation)
OperationQueue
ブロックを追加する:
myQueue.addOperation {
// some task
}
複数の追加Operation
にSをOperationQueue
:
let operations = [Operation]()
// Fill array with Operations
myQueue.addOperation(operations)
キュー内で同時に実行できるOperation
の数を調整します。
myQueue.maxConcurrentOperationCount = 3 // 3 operations may execute at once
// Sets number of concurrent operations based on current system conditions
myQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
キューを一時停止すると、既存の起動されていない操作やキューに追加された新しい操作の実行が開始されなくなります。このキューを再開する方法は、 isSuspended
をfalse
戻すことです。
myQueue.isSuspended = true
// Re-enable execution
myQueue.isSuspended = false
OperationQueue
一時停止しても、すでに実行中の操作は停止またはキャンセルされません。グローバルキューやメインキューではなく、作成したキューを一時停止する必要があります。
高レベル操作の作成
Foundationフレームワークは、キュー上で実行される可能性のある作業の一部をカプセル化する高水準オブジェクトを表すOperation
型を提供します。キューはこれらの操作のパフォーマンスを調整するだけでなく、操作間の依存関係を確立したり、取り消し可能な操作を作成したり、操作キューによって使用される並行性の程度を制限したりすることもできます。
Operation
、すべての依存関係の実行が終了した時点で実行可能になります。 isReady
プロパティはtrue
変わりtrue
。
単純な非並行Operation
サブクラスを作成します。
class MyOperation: Operation {
init(<parameters>) {
// Do any setup work here
}
override func main() {
// Perform the task
}
}
class MyOperation: NSOperation {
init(<parameters>) {
// Do any setup work here
}
override func main() {
// Perform the task
}
}
OperationQueue
操作を追加する:
myQueue.addOperation(operation)
これにより、キュー上で同時に操作が実行されます。
Operation
依存関係を管理する。
依存関係は、そのOperation
が実行準備が整う前にキュー上で実行しなければならない他のOperation
定義します。
operation2.addDependency(operation1)
operation2.removeDependency(operation1)
キューなしでOperation
を実行する:
operation.start()
依存関係は無視されます。これがコンカレント・オペレーションである場合、そのstart
メソッドがバックグラウンド・キューにオフロードされた場合、タスクは同時に実行される可能性があります。
並行運用。
Operation
が実行するタスクがそれ自体では非同期(例: URLSession
データタスク)である場合、 Operation
を並行操作として実装する必要があります。この場合、 isAsynchronous
実装はtrue
を返す必要があります。通常、いくつかの設定を実行するstart
メソッドがあり、実際にタスクを実行するmain
メソッドが呼び出されます。
非同期Operation
実装を開始するときは、 isExecuting
、 isFinished
メソッドおよびKVOを実装する必要があります。したがって、実行が開始されると、 isExecuting
プロパティがtrue
変更されtrue
。ときOperation
そのタスクを終了し、 isExecuting
に設定されているfalse
、そしてisFinished
に設定されているtrue
。操作は、それが両方のキャンセルされた場合isCancelled
とisFinished
に変更true
。これらのプロパティはすべてキー値で観測可能です。
Operation
。
cancel
呼び出すと、 isCancelled
プロパティがtrue
変更されtrue
。あなた自身のOperation
サブクラス内からのキャンセルに応答するには、少なくともmain
内のisCancelled
の値を定期的にチェックし、適切に応答する必要があります。
operation.cancel()