Grand Central Dispatch(GCD)を使った並列処理が実行される順番を調べました。
やりたいこととしては、別スレッド上で処理してすべての処理が完了したらUIスレッドに終わった通知を送りたい。つまり並列処理の待ち合わせ(並列プログラミングガイド的にいえば「スレッドの合流」)をしなければいけません。
使えるのは、dispatch_group_async関数
とdispatch_apply関数
でしょうか。
dispatch_group_asyncを使ってスレッドの合流をする
GCDを使って非同期にするためにはdispatch_sync
やdispatch_async関数
を使うのがポピュラーです。ただこれらは単一のブロックしか処理できません。複数の非同期処理を管理するためにはdispatch_group_xxx関数
を使用します。
dispatch_group_create関数でdispatch_group_t
を生成して、dispatch_group_async関数で非同期したい処理を実装して、すべての処理が完了するのをdispatch_group_wait関数で待って、すべての処理が完了したらdispatch_group_notify関数で通知するという流れです。
let group: dispatch_group_t = dispatch_group_create() for i in 0..<2 { dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in NSLog("proc start \(i)") // なんかの重い処理 NSLog("proc end \(i)") } } dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in NSLog("notify") } NSLog("before wait") dispatch_group_wait(group, DISPATCH_TIME_FOREVER) NSLog("after wait")
上記のコードの結果です。ログの出力にNSLogを使っているのはスレッドがロックされるので実行順がわかりやすいからですね。実際には「proc start 0」「proc start 1」あたりが同時に実行されます。
2015-06-13 13:17:04.944 piyo[3681:242285] before wait 2015-06-13 13:17:04.944 piyo[3681:242352] proc start 0 2015-06-13 13:17:04.944 piyo[3681:242353] proc start 1 2015-06-13 13:17:04.944 piyo[3681:242352] proc end 0 2015-06-13 13:17:05.046 piyo[3681:242353] proc end 1 2015-06-13 13:17:05.046 piyo[3681:242285] after wait 2015-06-13 13:17:05.046 piyo[3681:242353] notify
上記のログの場合242285がmainスレッドかな。dispatch_group_notify関数は最後に走ったスレッド上(上記のログの場合は242353)で実行されるみたい。
dispatch_applyを使ってスレッドの合流をする
dispatch_apply関数
は0〜10までを高速に並列処理することができます。dispatch_applyは処理が終わるまでスレッドをロックしますので、すべての処理が完了するのを待ちます。
NSLog("before apply") dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { (i) -> Void in NSLog("proc start \(i)") // なんかの重い処理 NSLog("proc end \(i)") } NSLog("after apply")
上記のコードの結果です。
2015-06-14 12:56:43.050 piyo[7332:744402] before apply 2015-06-14 12:56:43.051 piyo[7332:744402] proc start 3 2015-06-14 12:56:43.051 piyo[7332:744461] proc start 2 2015-06-14 12:56:43.051 piyo[7332:744472] proc start 7 2015-06-14 12:56:43.051 piyo[7332:744473] proc start 6 2015-06-14 12:56:43.051 piyo[7332:744471] proc start 4 2015-06-14 12:56:43.051 piyo[7332:744460] proc start 0 2015-06-14 12:56:43.051 piyo[7332:744470] proc start 5 2015-06-14 12:56:43.051 piyo[7332:744459] proc start 1 2015-06-14 12:56:44.052 piyo[7332:744461] proc end 2 2015-06-14 12:56:44.052 piyo[7332:744473] proc end 6 2015-06-14 12:56:44.052 piyo[7332:744402] proc end 3 2015-06-14 12:56:44.052 piyo[7332:744472] proc end 7 2015-06-14 12:56:44.052 piyo[7332:744459] proc end 1 2015-06-14 12:56:44.052 piyo[7332:744470] proc end 5 2015-06-14 12:56:44.052 piyo[7332:744471] proc end 4 2015-06-14 12:56:44.052 piyo[7332:744460] proc end 0 2015-06-14 12:56:44.052 piyo[7332:744461] proc start 8 2015-06-14 12:56:44.052 piyo[7332:744473] proc start 9 2015-06-14 12:56:45.053 piyo[7332:744473] proc end 9 2015-06-14 12:56:45.053 piyo[7332:744461] proc end 8 2015-06-14 12:56:45.053 piyo[7332:744402] after apply
挙動がちょっと変わっているのですが、一気に10個の処理が走るわけではなくて8個の処理を終えてから、残りの2個の処理を実行しています。同時にいくつ処理をさせるか自体についてはプログラマブルではないのでデバイスに応じて最適な数の並列処理を実行してくれるようです。
dispatch_applyの挙動について調べたところによると、過去の時点ではdispatch_applyは同時に2個ずつしか処理してくれなかったと記事を書いている方がいました。
参照
関連記事
本記事以外にも iOSアプリ開発に役立つ情報をまとめています。