Sponsor Link
環境&対象
- macOS Big Sur 11.4
- Xcode 12.5
- iOS 14.5
Swift5.5 で、Concurrency が導入されました。
async/await を使用して記述する方が、デッドロック等を防ぐことにもつながりますので、おすすめです。
DispatchSemaphore
セマフォ
並列処理でのアクセス制御を管理するための仕組みです。
具体的には、限られたリソース等に 競合することなくアクセスするための制御手段の概念として、セマフォ というものがあります。
Wikipedia は、こちら。
DispatchSemaphore
DispatchSemaphore は、上記のセマフォを実装したものです。
セマフォの中でも複数個の資源を扱うことのできる カウンティングセマフォです。
同じオブジェクトを参照しなくてはいけないので、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()
- セマフォを用意します(初期値0として、作成します)
- ここに到達すると .signal をコールすることで、セマフォが+1され、1となります
- セマフォの初期値は0ですので、(2) で.signal がコールされセマフォが 1になるまで待ちます
PhotoLibrary へのアクセスそのものについては、以下を参照ください。
[iOS] PHPhotoLibrary.requestAuthorization(_:) に入った変更 @iOS14
上限が1でないセマフォを使うと最大同時アクセス数の制御
上記は、カウントが0か1のセマフォですが、1より大きい数字を指定して、最大同時アクセス数のようなものを制御をすることも可能です。
まとめ:DispatchSemaphore の使い方
- DispatchSemaphore は、実装されたカウンティングセマフォ
- signal で +1 される
- wait で −1しようとして、できなければ、待つ
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link