2016年9月2日金曜日

非同期処理を同期処理に(dispatch_semaphore)

時として不便な非同期処理

画像ライブラリから1枚ずつ画像を読み込んで処理するコードを書いていたのだが、読み込み処理がblock構文を使った非同期処理なので、forループばかりどんどん空回りしてblock内の処理が後回しになり、期待通りの結果になってくれなかった。

for (int i = 0; i < imageURL.count; i++) {
      ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
      [library assetForURL:imageURL[i] resultBlock:^(ALAsset *asset) {
            //ここに読み込み成功時の処理を書く
      } failureBlock:^(NSError *error) {
            //ここにエラー処理を書く
      }];
}

非同期処理を同期処理にする技

これを、処理が一件終わるまで待たせるようにするのが同期処理だ。
dispatch_semaphore(ディスパッチ・セマフォ)ってのを使う。
処理の終了待ちを意味するフラグかカウンターみたいなものをセットして並列処理を制限し、終わった時点でそれをクリアしてやるのだな。

for (int i = 0; i < imageURL.count; i++) {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
      [library assetForURL:tempURLArray[i] resultBlock:^(ALAsset *asset) {
          //ここに読み込み成功時の処理を書く
          dispatch_semaphore_signal(semaphore);
      } failureBlock:^(NSError *error) {
          //ここにエラー処理を書く
          dispatch_semaphore_signal(semaphore);
      }];
   });
      dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

dispatch_semaphore_create(0)でセマフォを作り、
dispatch_get_global_queue()内のblock構文の中に同期処理させたいコードを書き、
処理が済んだ時点でdispatch_semaphore_signal(semaphore)を実行して次の処理に移っている。
dispatch_semaphore_wait()はどれくらい処理を待つかだ。ここでは処理が終わるまでずっと待たせるようにしている。
古い参考サイトでは最後に変数semaphoreを解放してやる必要があるってなってたけど、ARC使ってる分には勝手に解放してくれるからいらない。

dispatch_async()ってのはasyncってくらいだから本来非同期処理にするための命令らしい(だから並列処理の優先順位をDISPATCH_QUEUE_PRIORITY_BACKGROUNDって指定してる)。
でもこう書くみたいだからいいの! 動いたからいいの! 他の参考サイトでもそう書いてるからいいの!

これを応用すれば同期処理も非同期処理に変更できるようだけど、それはまたいつか。

0 件のコメント:

コメントを投稿