Swift Atomicsは、システムプログラマーがSwiftで直接、同期構造を記述できるようにすることを目的としている。
Swift Atomicsによって、ロックフリーデータ構造などの洗練された高レベルのマルチスレッド構造を構築することを目指す開発者は、Swiftで直接それを実施できる機会を得る。以前は、Swiftでアトミック操作を使用するには、開発者は通常、別の言語で実装された外部ライブラリに頼らなければならなかった。
Swiftは、Swift開発者がatomic操作を使いやすくするよう努めているが、低レベルの同期プリミティブを使用するプログラムに固有のリスクを排除するものではなく、すべてのSwift開発者向けのライブラリとなることを意図したものではない。
Atomicsパッケージは、Swift APIの確立された設計原則に従う、atomic操作用に慎重に検討されたAPIを提供します。ただし、基礎となる操作は非常に低レベルの抽象の上で機能します。Atomicsは、他の低レベルの並行性構造よりもさらに、正しく使用するのが難しいことで有名です。
そのため、ほとんどのSwift開発者は、非同期コードのためにGrand Central Dispatchとキューを使い続けるであろう。
以下は、Swift Atomicsを使用して、複数のスレッドで共有されるカウンターをインクリメントするプログラムを作成する方法の例である。
import Atomics
import Dispatch
let counter = ManagedAtomic<Int>(0)
DispatchQueue.concurrentPerform(iterations: 10) { _ in
for _ in 0 ..< 1_000_000 {
counter.wrappingIncrement(by: 1, ordering: .relaxed)
}
}
counter.load(ordering: .relaxed) // ⟹ 10_000_000
ManagedAtomic
は、通常の参照カウント動作をサポートしているため、atomic値を使用する最も簡単な方法である。これが過度のオーバーヘッドである場合は、UnsafeAtomic
を使用できる。これは、ポインタを取得できる任意のメモリで使用できる。ManagedAtomic
とUnsafeAtomic
はどちらも、load
、store
、exchange
、compareExchange
、weakCompareExchange
など、同じ操作セットを提供する。
Swift Atomicsは、上記の例に示すように、Int
をサポートするだけでなく、整数と符号なし整数ファミリー全体、ブール値、ポインター、参照、列挙型など、他の多くのタイプをサポートする。
atomic操作を使用するコードは、Swiftの排他的メモリアクセスに結び付くものではない。これにより、プログラムの別の部分で変数が変更されているときに、その変数が別名前でアクセス開始されることを防ぐ。Swift 5を使用する場合、排他的アクセスルールに違反すると通常クラッシュが発生するが、atomic操作の処理は異なる。これはSwiftメモリモデルを緩和することで可能になる。SwiftメモリモデルはC/C++からインポートされた低レベルのプリミティブと相互運用できるようにすることを目的としたものである。そのため、Swift Exclusivity Accessを管理する新しいルールでは、次のように述べられている。
同じ変数への2つのアクセスは、両方のアクセスが読み取りであるか、両方のアクセスがatomicでない限り、オーバーラップすることはできません。
上の文では、atomicアクセスは、C atomic操作ライブラリに属するいくつかの関数の呼び出しとして定義されている。
Swift Atomicsは、並行プログラミングに対するSwiftの適合性を拡張する。しかし、async/awaitとactorsに対して何らかのサポートをする本当の並行性モデルに関する取り組みは、まだSwift 6に向けて進行中である。今後数か月で、Swiftチームはタグ付きatomicを導入し、atomic浮動小数点演算をサポートする。