SwiftのActorはactorとして宣言する方法とActor属性でマークする方法があります。
この2つの使い方では様々な違いがあります。
ActorはActorインスタンス毎に排他制御されます。
まずは分かりやすくactorで宣言した場合です。
actor ActorSample:ObservableObject{
var count = 0
func countUp(){
sleep(5)
count += 1
print(count)
}
}
func testFunc(){
let actor1 = ActorSample()
let actor2 = ActorSample()
Task{
await actor1.countUp()
}
Task{
await actor2.countUp()
}
}
この場合は共にActorSampleのactorを使っていますが、
インスタンスはそれぞれ別のものを使用しています。
その為、この2つのcountUpは並列で実行されます。
次にActor属性でマークした場合は見てみましょう。
@globalActor
actor ActorDifference {
static var shared = ActorDifference()
}
@ActorDifference
class ActorMarkedClass {
var count = 0
func countUp(){
sleep(5)
count += 1
print(count)
}
}
func markedTestFunc(){
Task{
let class1 = await ActorMarkedClass()
await class1.countUp()
}
Task{
let class2 = await ActorMarkedClass()
await class2.countUp()
}
}
この場合はクラスのインスタンスは共通ですが、
actorのインスタンスはActorDifference.sharedが参照される為、同じになります。
その為、2つのcountUpは排他で実行されます。
また、Actor属性でマークした場合はイニシャライザも非同期で実行する必要があります。
この様にactorで宣言するかActor属性でマークするかで挙動が変わって来ます。
排他制御すべき内容を考え、どちらを使用するかしっかりと判断しましょう。
余談ですが、今回はActorDifference.sharedをvarで宣言したのでこんな例も用意しました。
func markedTestFunc2(){
Task{
let class1 = await ActorMarkedClass()
await class1.countUp()
}
Task{
ActorDifference.shared = ActorDifference()
let class2 = await ActorMarkedClass()
await class2.countUp()
}
}
Actorのインスタンスを変えてみました。
これで排他制御されずに並列実行されます。
試して見たところ、TaskとTaskの間で行うと排他制御されました。
勿論Task内で変えないといけない訳ではなく、以下の例では問題ありませんでした。
Button("CountUp1"){
Task {
await actorMarkedClass.countUp()
}
}
Button("CountUp2"){
ActorDifference.shared = ActorDifference()
Task {
await actorMarkedClass2.countUp()
}
}
挙動が把握しきれていないので、
Actor属性を定義する時はletで定義する方が無難でしょう。
どうしてもこの様な処理を行う場合はしっかり確認しましょう。
コメント