iOS
GCD(グランドセントラルディスパッチ)
サーチ…
前書き
Grand Central Dispatch(GCD)はマルチスレッドに対するアップルの答えです。キュー内で同期または非同期にタスクを実行するための軽量フレームワークで、バックグラウンドでCPUスレッドを処理します。
関連トピック: 同時実行性
ディスパッチキューを作成する
dispatch_queue_create
を使用dispatch_queue_create
て独自のキューを作成できます
目標-C
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL);
迅速
// Before Swift 3
let queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL)
// Swift 3
let queue = DispatchQueue(label: "com.example.myqueue") //default is serial queue, unless .concurrent is specified as an attribute otherwise
メインキューの取得
メインキューは、すべてのUI更新が行われ、UI変更を伴うコードが配置されるディスパッチキューです。
NSURLSession
ような非同期プロセスの完了時にUIを更新するには、メインキューにNSURLSession
する必要があります
メイン・キュー・コールには、 synchronous
とasynchronous
型の2種類がありasynchronous
。 synchronously
に何かを呼び出すと、その操作を開始したスレッドは、タスクが完了するのを待ってから続行します。 Asynchronous
は、待機しないことを意味します。
コード目的 - C
Synchronous
メインキューコール
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Asynchronous
メインキューコール
dispatch_async(dispatch_get_main_queue(), ^{
// do work here to Usually to update the User Interface
});
SWIFT 3
Asynchronous
メインキューコール
DispatchQueue.main.async {
}
Synchronous
メインキューコール
DispatchQueue.main.sync {
}
派遣グループ
DispatchGroupは、作業の集約を可能にします。それらを使用して、複数の異なる作業項目を提出し、異なるキューで実行する可能性がある場合でも、完了した時点を追跡できます。この動作は、すべての指定されたタスクが完了するまで進行できない場合に役立ちます。
これが役に立つ場合のシナリオは、続行する前に完了する必要のある複数のWebサービス呼び出しがある場合です。たとえば、いくつかの関数で処理する必要がある複数のデータセットをダウンロードする必要があります。受信したすべてのデータを処理する関数を呼び出す前に、すべてのWebサービスが完了するまで待つ必要があります。
スウィフト3
func doLongTasksAndWait () {
print("starting long running tasks")
let group = DispatchGroup() //create a group for a bunch of tasks we are about to do
for i in 0...3 { //launch a bunch of tasks (eg a bunch of webservice calls that all need to be finished before proceeding to the next ViewController)
group.enter() //let the group know that something is being added
DispatchQueue.global().async { //run tasks on a background thread
sleep(arc4random() % 4) //do some long task eg webservice or database lookup (here we are just sleeping for a random amount of time for demonstration purposes)
print("long task \(i) done!")
group.leave() //let group know that the task is finished
}
}
group.wait() //will block whatever thread we are on here until all the above tasks have finished (so maybe dont use this function on your main thread)
print("all tasks done!")
}
また、グループが終了するのを待たずに、すべてのタスクが完了したら関数を実行したい場合は、 group.wait()
代わりにnotify
関数を使用してgroup.wait()
group.notify(queue: DispatchQueue.main) { //the queue: parameter is which queue this block will run on, if you need to do UI updates, use the main queue
print("all tasks done!") //this will execute when all tasks have left the group
}
出力例:
starting long running tasks
long task 0 done!
long task 3 done!
long task 1 done!
long task 2 done!
all tasks done!
詳細については、 Apple Docsまたは関連トピックを参照してください。
セマフォディスパッチ
DispatchSemaphoreは、従来の計数セマフォの効率的な実装を提供します。これは、複数の実行コンテキストにわたるリソースへのアクセスを制御するために使用できます。
セマフォを使用する場合のシナリオは、複数のタスクがファイルから同時に読み書きしようとしている場合に、ファイルの読み込み/書き込みを行う場合、パフォーマンスが向上し、各タスクが次のように待機するようになる可能性があります。 I / Oコントローラを過負荷にしないようにします。
スウィフト3
func do2TasksAtATime () {
print("starting long running tasks (2 at a time)")
let sem = DispatchSemaphore(value: 2) //this semaphore only allows 2 tasks to run at the same time (the resource count)
for i in 0...7 { //launch a bunch of tasks
DispatchQueue.global().async { //run tasks on a background thread
sem.wait() //wait here if no resources available
sleep(2) //do some long task eg file access (here we are just sleeping for a 2 seconds for demonstration purposes)
print("long task \(i) done! \(Date())")
sem.signal() //let the semaphore know this resource is now available
}
}
}
出力例:(タイムスタンプに気づく)
starting long running tasks (2 at a time)
long task 0 done! 2017-02-16 07:11:53 +0000
long task 1 done! 2017-02-16 07:11:53 +0000
long task 2 done! 2017-02-16 07:11:55 +0000
long task 3 done! 2017-02-16 07:11:55 +0000
long task 5 done! 2017-02-16 07:11:57 +0000
long task 4 done! 2017-02-16 07:11:57 +0000
long task 6 done! 2017-02-16 07:11:59 +0000
long task 7 done! 2017-02-16 07:11:59 +0000
詳細については、 Apple Docs
シリアルと並行ディスパッチキュー
スウィフト3
シリアルキュー
func serialQueues () {
let serialQueue = DispatchQueue(label: "com.example.serial") //default queue type is a serial queue
let start = Date ()
for i in 0...3 { //launch a bunch of tasks
serialQueue.async { //run tasks on a background thread, using our serial queue
sleep(2) //do some long task eg webservice or database lookup
let timeTaken = Date().timeIntervalSince(start)
print("serial long task \(i) done! total time taken: \(timeTaken)")
}
}
}
出力例:
serial long task 0 done! total time taken: 2.07241100072861
serial long task 1 done! total time taken: 4.16347700357437
serial long task 2 done! total time taken: 6.23209798336029
serial long task 3 done! total time taken: 8.30682599544525
並行キュー
func concurrentQueues () {
let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent) //explicitly specify the queue to be a concurrent queue
let start = Date ()
for i in 0...3 { //launch a bunch of tasks
concurrentQueue.async { //run tasks on a background thread, using our concurrent queue
sleep(2) //do some long task eg webservice or database lookup
let timeTaken = Date().timeIntervalSince(start)
print("concurrent long task \(i) done! total time taken: \(timeTaken)")
}
}
}
出力例:
concurrent long task 3 done! total time taken: 2.07092100381851
concurrent long task 0 done! total time taken: 2.07087397575378
concurrent long task 2 done! total time taken: 2.07086700201035
concurrent long task 1 done! total time taken: 2.07089096307755
討論
上記の例からわかるように、シリアルキューは各タスクをキューに提出された順に完了します。各タスクは、前のタスクが完了するのを待ってから実行します。並行キューの場合、各タスクはキュー内の他のタスクを待機せず、できるだけ早く実行します。利点は、キュー上のすべてのタスクが別々のスレッドで同時に実行されるため、並行キューがシリアルキューより時間がかかりにくくなることです。
タスクの実行順序が重要でない場合は、常に最高効率のために並列キューを使用してください。