ラベル 同期処理 の投稿を表示しています。 すべての投稿を表示
ラベル 同期処理 の投稿を表示しています。 すべての投稿を表示

2019年6月20日木曜日

Swift3のDispatch semaphore

同期処理や非同期処理などを制御する仕組み。
Objective-Cと書き方がだいぶ変わったみたいなんでまとめる。
まだよくわかってないんで、書きかけとする。

言葉の意味

dispatch = 使者や手紙などを送る
semaphore = 手旗信号

要するに処理待ちのカウンターみたいなもので、値がマイナスの間はスレッドが停止する。
希望の処理が完了したら値をプラスしてやり、それが0になったらスレッド再開。

書式

詳しくは公式サイトで。

semaphoreを作る

let semaphore = DispatchSemaphore(value: 0)

semaphoreを通知、または増やす


semaphore.signal()

semaphoreを待つ、または減らす


semaphore.wait()
_ = semaphore.wait(timeout: DispatchTime.distantFuture
semaphore.wait(wallTimeout: DispatchWallTime)

同期処理させたいコードを書くところ

DispatchQueue.global(qos: .background).async { ここ }

具体的書き方

  1. semaphoreを作る
  2. DispatchQueue.global(qos: ここ).async { }の引数に優先順位を書く。
  3. DispatchQueue.global(qos: .xxxxx).async { ここ }のクロージャ内に同期処理させたいコードを書く
  4. 処理が一件終了したらsemaphore.signal( )を実行して待ち行列を解消して次の処理に移る。
  5. クロージャの最後にどれくらい待つのかを書く(たとえば処理が終わるまでずっと(以前はFOREVERとかなんとか書いてたけど、今はdistantFuture(遠い未来)だそうだ)とか)

let semaphore = DispatchSemaphore(value: 0)

_ = semaphore.wait(timeout: DispatchTime.distantFuture)

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(ディスパッチ・セマフォ)ってのを使う。

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って指定してる)。
でもこう書くみたいだからいいの! 動いたからいいの! 他の参考サイトでもそう書いてるからいいの!

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

Swift3から(だと思う)は、書き方がObjective-Cの書き方とだいぶ変わってしまったので、別にまとめる。