[Swift] Semaphoe の使い方

Swift

     

TAGS:

⌛️ < 1 min.
非同期処理を、同期で処理したい時に使うことができる DispatchSemaphore を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Big Sur 11.4
  • Xcode 12.5
  • iOS 14.5
注記

Swift5.5 で、Concurrency が導入されました。
async/await を使用して記述する方が、デッドロック等を防ぐことにもつながりますので、おすすめです。

DispatchSemaphore

セマフォ

並列処理でのアクセス制御を管理するための仕組みです。

具体的には、限られたリソース等に 競合することなくアクセスするための制御手段の概念として、セマフォ というものがあります。

Wikipedia は、こちら

DispatchSemaphore

DispatchSemaphore は、上記のセマフォを実装したものです。

セマフォの中でも複数個の資源を扱うことのできる カウンティングセマフォです。

MEMO

同じオブジェクトを参照しなくてはいけないので、struct ではなく、class になっています。

Apple のドキュメントは、こちら

DispatchSemaphore の使い方

.signal を呼ぶと、カウントが +1 され、.wait を呼ぶと、カウントが −1 されます。

イメージとしては、セマフォは ”利用可能リソース数”を保持しているので、.signal は、リソース使用終了の宣言です。
.wait は、リソース使用開始の宣言ですが、その時に、カウンターが 0 であれば、別のスレッド等から .signal されて、利用可能リソースが発生するのを待つことになります。

必要になるケース

Swift で普通に statement を書いていくと、同期処理ですので、DispatchSemaphore を使う必要は発生しません。

Completion Handler 等の実行を待ちたいケースが、DispatchSemaphore を使いたくなるケースの1つです。

例えば、以下は、Photo ライブラリへのアクセス許可を求めるコードです。


    PHPhotoLibrary.requestAuthorization(for: .readwrite) { status in
        // (1)
        if status == .authorized {
            // ok to access
        } else if status == .denied {
            // rejected....
        }
    }
    // (2)

requestAuthorization の結果は、非同期で通知されるので、(1) のコードが、(2) のコードの前に実行されている保証はありません。

UI と内部処理でそれぞれお互いに ブロックしない ことは大事ですが、ケースによっては、一方(もしくは、両方)の終了を待って実行したいことがあります。

このようなケースで、DispatchSemaphore を使うと 一方(もしくは両方)の終了をまって実行させることができます。


    // (1)
    let semaphore = DispatchSemaphore(value: 0)
    PHPhotoLibrary.requestAuthorization(for: .readwrite) { status in
        if status == .authorized {
            // ok to access
        } else if status == .denied {
            // rejected....
        }
        // (2)
        semaphore.signal()
    }
    // (3)
    semaphore.wait()
コード解説
  1. セマフォを用意します(初期値0として、作成します)
  2. ここに到達すると .signal をコールすることで、セマフォが+1され、1となります
  3. セマフォの初期値は0ですので、(2) で.signal がコールされセマフォが 1になるまで待ちます

PhotoLibrary へのアクセスそのものについては、以下を参照ください。
[iOS] PHPhotoLibrary.requestAuthorization(_:) に入った変更 @iOS14

上限が1でないセマフォを使うと最大同時アクセス数の制御

上記は、カウントが0か1のセマフォですが、1より大きい数字を指定して、最大同時アクセス数のようなものを制御をすることも可能です。

まとめ:DispatchSemaphore の使い方

DispatchSemaphore の使い方
  • DispatchSemaphore は、実装されたカウンティングセマフォ
  • signal で +1 される
  • wait で −1しようとして、できなければ、待つ

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です