カテゴリー: iOS

[Swift5.5]actorで非同期のデータ競合を回避しよう!

はじめに

Swift5.5にて非同期に関する追加が行われ、async/awaitについては以前の記事で紹介しましたが、今回は同じく非同期で活躍するactorについて触れたいと思います。

以前の記事はこちらから。
Swift5.5での非同期処理async/awaitの追加

actor

actorはSwiftコンパイラーにて、actorに伴う制限を静的に適用し、可変データへの同時アクセスを防ぐことができます。それでは実際に使用しながら見ていきたいと思います。

actor TestActor {
    let name = "aaa"
    var number: Int = 0
}

上記のようにactorを作成してみました、実際にコードで呼んでみると

このように値の変更が不可であるnameは何も制約を付けずとも呼び出せるのですが、値の変更が可能なnumberはそのままではエラーになってしまいます。エラーを発生させずにnumberにアクセスするには以下のように非同期でアクセスしなければなりません。

Task {
    await print(testActor.number)
}

このようにactorを用いることで、データ競合が起こりうる可能性があるパラメータや、それに伴うメソッドを管理することで、整合性を保とうというわけです。

というわけで、actorにはメソッドも追加できます。

actor TestActor {
    let name = "aaa"
    var number: Int = 0
    func numberInc() -> Int {
        number += 1
        return number
    }
    
    func numberDec() -> Int {
        number -= 1
        return number
    }
}

メソッドも同じく、非同期で呼び出さないとコンパイラでエラーが表示されます。

ただそこに気をつければ使用方法は他のasyncメソッドと変わりないです。

どのような動きをするかサンプルを作成しました。

func testActorNumberInc() async -> Int {
    async let a = testActor.numberInc()
    async let b = testActor.numberInc()
    async let c = testActor.numberInc()
    print("number=",await testActor.number)
    await print("SUM=", a + b + c)
    print("number=",await testActor.number)
    return await testActor.number
}

上記の処理を実行すると以下のログが出力されます。

number= 0
SUM= 6
number= 3

a,b,cをawaitする前にnumberを出力しているので一旦初期値の0が表示されることを確認できると思います。

actorを使用する際の注意

actorを使用することにより、データの競合が発生する確率を下げれるとは思いますが、データの同期などは自分で行わなくてはなりません。例えば以下のように別スレッドで処理を実行した場合、

queueOne.async {
    await testActor.numberInc()
}
queueTwo.async {
    print("number=",await testActor.number)
}

どちらのスレッドから開始されるかはわからないため、出力させる値が0なのか1なのかわからないです。処理やデータの構造がこのような形で問題ない作りならば良いですが、スレッドが複雑に動き出した時に予期しないケースが発生することもあります。その際はデータの同期の仕組みを考えたり等、現在の作りと変わらないことは、頭に入れておいたほうが良いと思います。

さいごに

actorを使用することにより、非同期で使用する前提のデータ構造や、それらを操作するメソッドを作成できるので、使用者がコンパイラエラーで同期的に使用するのを防げたり、第三者がコードをみた際もわかりやすくなると思うので、async/awaitを使用する際はactorはセットが必須かなと思いました。

おすすめ書籍

nukky

シェア
執筆者:
nukky
タグ: SwiftiOS

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前