actor
actorはSwift5.5から導入された、データ競合を守るための新しい型です。
データ競合とは複数のスレッドで同一のデータ領域にアクセスした場合に、データに不整合が生じることを言います。
マルチスレッドで別々の処理を並列で実行することは便利ですが、一つずつ実行する逐次的な処理ではないからこそデータ不整合には気をつける必要があります。
今回はactorを使いどのようにデータ競合から守るのか紹介していきます。
actorを使用しない場合
以下のTimeクラスのupdate処理を別々のスレッドで叩いてる処理になります。
各々のスレッド処理でupdate後にベストタイムを出力してますが、出力結果としては順不同で15.3, 13.0が出力されます。
この順不同な結果が出るのはデータ競合が起きているためです。
本来なら各々のスレッドでupdateした値が出力結果になるはずです。
それではこの処理をactorを使い修正していきます。
class Time {
var times: [Double] = []
private(set) var bestTime: Double = 100
func update(with time: Double) {
times.append(time)
if time < bestTime {
bestTime = time
}
}
}
let time = Time()
DispatchQueue.global(qos: .default).async {
time.update(with: 15.3)
print(time.bestTime)
}
DispatchQueue.global(qos: .default).async {
time.update(with: 13.0)
print(time.bestTime)
}
actorを使用する場合
以下がactorを使用した処理になります。
変更点としては、Timeをclassからactorで実装し直します。
次に呼び出し側の処理として、複数同時アクセスするためにTask.detachedを使用します。
後はactor内のプロパティ、メソッドにアクセスするためにawaitを付与します。
これでインスタンスに対し、複数同時アクセスを行なってもデータ競合が発生しません。
actor Time {
var times: [Double] = []
private(set) var bestTime: Double = 100
func update(with time: Double) {
times.append(time)
if time < bestTime {
bestTime = time
}
}
}
let time = Time()
Task.detached {
await time.update(with: 15.3)
print(await time.bestTime)
}
Task.detached {
await time.update(with: 13.0)
print(await time.bestTime)
}
参考
今回は以下の記事を参考に備忘録として簡略化してまとめました。
より細かく知りたい方は以下の記事をご覧ください。
https://blog.personal-factory.com/2022/01/16/explanation-of-actor-since-swift5_5/
https://qiita.com/h1d3mun3/items/f6ae63368e133e212115