🦀

Swiftのactor(class)の動きを確認

2023/02/08に公開

actorの簡単な説明

classと同じように書けて、並列処理をした場合のデータの競合性を保証してくれるもの

確認するコード

Counterを用意して、1秒かけてcountに1足す関数update()関数を並列でよびだすというコードで動きを確認します。

class Counter {
  var count: Int = 0
  
  func update() async -> Int {
    try! await Task.sleep(for: .seconds(1))
    count += 1
    return count
  }
}

実行コード

並列コードにはasync letを使用している

以下のコードは1秒かかるupdate()を5つ実行しているが、並列(async let)で実行しているため、5秒ではなく、1秒で終了する

let counter = Counter()

async let value1 = counter.update()
async let value2 = counter.update()
async let value3 = counter.update()
async let value4 = counter.update()
async let value5 = counter.update()

let values = await [value1, value2, value3, value4, value5]

print(values)

実行結果(class)

何度か実行してみた結果

  • 結果内容の順番は並列で行われているため保証されていない
  • classではデータの競合が保証されていないため同じデータがある
[4, 2, 1, 5, 3]
[3, 1, 4, 2, 5]
[4, 3, 2, 2, 5] // `2`が2回出てしまっている

actorで実行

最初のコードCounterをclassからactorに置き換えます。
これを同じ実行コードで実行すると、2が2回出るなどのデータ競合は起こらなくなります。
これがactorの動きです。

コード全体

actor/*class*/ Counter {
  var count: Int = 0
  
  func update() async -> Int {
    try! await Task.sleep(for: .seconds(1))
    count += 1
    return count
  }
}

let counter = Counter()

async let value1 = counter.update()
async let value2 = counter.update()
async let value3 = counter.update()
async let value4 = counter.update()
async let value5 = counter.update()

let values = await [value1, value2, value3, value4, value5]

print(values)

Discussion