0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Swiftの@propertyWrapperを使ったスレッドセーフなAtomicプロパティとactorの比較

Last updated at Posted at 2025-03-10

Swiftには@propertyWrapperという便利な機能があり、プロパティの挙動をカプセル化できます。本記事では、スレッドセーフなプロパティラッパーAtomicを実装し、さらにactorとの違いを比較してみます。

Atomicの実装

以下のコードは、スレッドセーフなプロパティを実現するためにNSLockを利用したAtomicプロパティラッパーです。

import Foundation

@propertyWrapper
public struct Atomic<Value> {

    private var value: Value
    private let lock = NSLock()

    public init(wrappedValue value: Value) {
        self.value = value
    }

    public var wrappedValue: Value {
        get { return load() }
        set { store(newValue: newValue) }
    }

    func load() -> Value {
        lock.lock()
        defer { lock.unlock() }
        return value
    }

    mutating func store(newValue: Value) {
        lock.lock()
        defer { lock.unlock() }
        value = newValue
    }
}

Atomic の使い方

この Atomic プロパティラッパーは、以下のように利用できます。

class Counter {
    @Atomic var value: Int = 0
    
    func increment() {
        value += 1
    }
}

let counter = Counter()
DispatchQueue.concurrentPerform(iterations: 1000) { _ in
    counter.increment()
}

print(counter.value) // 1000 になるはず

actor を使った場合の実装

Swift 5.5 以降では、actor を使うことで、データ競合を防ぐことができます。

actor AtomicCounter {
    private var value: Int = 0
    
    func increment() {
        value += 1
    }
    
    func getValue() -> Int {
        return value
    }
}

actor の使い方

let counter = AtomicCounter()

Task {
    await counter.increment()
    print(await counter.getValue()) // 1
}

Atomic (NSLock) と actor の違い

特性 Atomic (NSLock) actor
スレッドセーフ NSLock を用いた明示的なロック制御 actor による自動的なデータ分離
排他制御 lock.lock() による同期処理 await による逐次処理
データ競合回避 明示的にロックを確保しながら値を読み書き actor 内部の状態はデフォルトでスレッドセーフ
非同期対応 基本的に同期処理 (NSLock は非同期に適さない) async/await に対応し、スレッドをブロックしない
パフォーマンス ロックのオーバーヘッドがあるが、高速に動作 actor はタスクの順番待ちが発生するため、状況によっては遅くなることがある
スレッド制約 NSLock は特定のスレッドでロック/アンロックを制御 actor のプロパティには他のスレッドから直接アクセスできない

Atomic vs actor の使い分け

状況 Atomic (NSLock) を使うべきケース actor を使うべきケース
同期処理 シンプルなスレッドセーフな値の読み書きを行う スレッド間で安全にデータを管理しつつ非同期処理を活用する
パフォーマンス ロックが短時間で済む場合 (Atomic のほうが高速) 並行処理が多く、タスク順序管理が必要な場合
可読性 @propertyWrapper を利用し、通常のプロパティのように扱いたい await を活用しながら安全に非同期データ管理を行いたい
非同期処理 Atomic は基本的に同期処理のため非同期には向かない actorasync/await で並行処理を管理できる

まとめ

@propertyWrapper を活用することで、スレッドセーフなプロパティを簡潔に管理できる Atomic について書いてみました。一方で、Swift 5.5 以降の actor を活用すれば、非同期環境でより安全にスレッド競合を防ぐこともできそうです。

  • 単純なスレッドセーフな値の管理Atomic (NSLock)
  • 非同期タスクと組み合わせる場合actor
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?