本软件包为 Swift 实现了一个原子操作库,为各种 Swift 类型(包括整数和指针值)提供原子操作。目标是使勇敢的开发者能够直接在 Swift 中构建同步结构。
原子操作不受通常的互斥规则约束。只要所有此类访问都通过原子操作完成,就可以安全地从多个并发执行线程读取和更新同一内存位置。例如,这是一个简单的原子计数器:
import Atomics
import Dispatch
let counter = ManagedAtomic<Int>(0)
DispatchQueue.concurrentPerform(iterations: 10) { _ in
for _ in 0 ..< 1_000_000 {
counter.wrappingIncrement(ordering: .relaxed)
}
}
counter.load(ordering: .relaxed) // ⟹ 10_000_000
访问计数器值的唯一方法是使用 ManagedAtomic
提供的方法之一,每种方法都实现特定的原子操作,并且每种方法都需要显式的排序值。(Swift 支持 C/C++ 内存排序的子集。)
原子值是管理并发的基础。但是,它们的级别太低,不能轻易使用。这些东西充满了陷阱。正确使用它们非常困难——比方说,比使用不安全指针更棘手。
处理原子操作的最佳方法是避免直接使用它们。只要有可能,总是最好依赖更高级别的构造。
此软件包的存在是为了支持少数无法避免使用原子操作的情况——例如,在实现那些高级同步/并发构造时。
主要重点是为系统程序员提供原子操作的访问权限,其 API 设计强调清晰度而不是表面的便利性
客户端代码使用清晰、未缩写的名称调用每个原子操作,该名称直接指定该操作的作用。原子操作绝不是隐式的——它们总是被清楚地说明。
没有默认的内存排序,以避免意外(且代价高昂)地使用顺序一致性。(这在 C/C++ 中是一个非常常见的问题。)
比较/交换等操作倾向于将输入值与结果清晰地分离。没有 inout
参数。
要在您自己的项目中使用 Atomics
,您需要将其设置为包依赖项
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MyPackage",
dependencies: [
.package(
url: "https://github.com/apple/swift-atomics.git",
.upToNextMajor(from: "1.2.0") // or `.upToNextMinor
)
],
targets: [
.target(
name: "MyTarget",
dependencies: [
.product(name: "Atomics", package: "swift-atomics")
]
)
]
)
原子操作的实现本质上比通常更紧密地与编译器耦合,因此,此软件包的标记版本通常只支持较窄范围的 Swift 版本
swift-atomics | 支持的 Swift 版本 |
---|---|
1.0.x |
5.3, 5.4, 5.5 |
1.1.x |
5.6, 5.7, 5.8 |
1.2.x |
5.7, 5.8, 5.9 |
main |
5.8、5.9、未发布的 5.10 和 trunk 快照 |
请注意,main
分支不是标记版本,因此其内容本质上是不稳定的。 因此,我们不建议在生产中使用它。(例如,它支持的 Swift 版本集可能会在不通知的情况下更改。)
Swift Atomics 软件包的源代码是稳定的。 版本号遵循 语义化版本控制——对公共 API 的源破坏性更改只能在新主版本中出现。
当前版本的 swift-atomics
软件包的公共 API 由在 Atomics
模块中标记为 public
的非下划线声明组成。
“下划线声明”是指在其完全限定名称的任何位置都带有前导下划线的声明。 例如,以下是一些即使在技术上标记为 public 也不会被视为公共 API 的名称:
FooModule.Bar._someMember(value:)
(带下划线的成员)FooModule._Bar.someMember
(带下划线的类型)_FooModule.Bar
(带下划线的模块)FooModule.Bar.init(_value:)
(带下划线的初始化器)不属于公共 API 的接口可能会在任何版本(包括补丁版本)中继续更改。
请注意,_AtomicsShims
模块的内容明确地不是公共 API。(如其带下划线的模块名称所暗示的那样。) 因此,其中的定义可能会随意更改,并且整个模块可能会在任何新版本中删除——不要直接导入此模块。 我们也不对 Utilities
、Tests
、Xcode
和 cmake
子目录的内容做出任何源代码兼容性承诺。
如果您有需要使用带下划线的 API 的用例,请提交功能请求进行描述! 我们希望公共接口尽可能有用——尽管最好是不损害安全性或限制未来发展。
未来软件包的次要版本可能会根据需要对这些规则进行更改。
我们希望此软件包能够快速采纳与其任务相关的 Swift 语言和工具链的改进。 因此,有时,此软件包的新版本将要求客户端升级到更新的 Swift 工具链版本。(这允许软件包利用新的语言/stdlib 功能、基于编译器错误修复进行构建,并尽快采用新的软件包管理器功能。)
要求新的 Swift 版本只需要一个次要版本号的增加。
有关此软件包提供的接口的详细概述,请参阅我们的 API 文档。
该软件包为以下 Swift 构造实现原子操作,所有这些构造都符合公共 AtomicValue
协议
Int
、Int64
、Int32
、Int16
、Int8
)UInt
、UInt64
、UInt32
、UInt16
、UInt8
)Bool
)UnsafeRawPointer
、UnsafeMutableRawPointer
、UnsafePointer<T>
、UnsafeMutablePointer<T>
)以及它们的可选包装形式(例如 Optional<UnsafePointer<T>>
)Unmanaged<T>
、Optional<Unmanaged<T>>
)DoubleWord
类型,它由两个 UInt
值 first
和 second
组成,提供双字宽的原子原语RawRepresentable
类型,其 RawValue
又是原子类型(例如,简单的自定义枚举类型)AtomicReference
协议)特别值得注意的是对原子强引用的完全支持。 这为并发数据结构提供了一种方便的内存回收解决方案,该解决方案与 Swift 的引用计数内存管理模型完美契合。(原子强引用是根据 DoubleWord
操作实现的。)但是,访问原子强引用(相对)代价高昂,因此,对于延迟初始化(但在其他方面是常量)的原子强引用的常见情况,我们还提供了一组单独的有效构造(ManagedAtomicLazyReference
和 UnsafeAtomicLazyReference
)。
保证此软件包公开的所有原子操作都具有无锁实现。 但是,我们不保证无等待操作——根据目标平台的功能,某些公开的操作可以通过比较和交换循环来实现。 也就是说,所有原子操作都直接映射到专用的 CPU 指令(如果可用)——达到 llvm & Clang 支持的程度。
原子访问是根据专用的原子存储表示形式实现的,这些表示形式与相应的常规(非原子)类型保持不同。(例如,上述计数器下的实际整数值无法直接访问。)这有几个优点:
虽然基于指针的底层原子操作作为静态方法在相应的 AtomicStorage
类型上公开,但我们强烈建议使用更高级别的原子包装器来管理准备/处理原子存储的细节。 此版本的库提供了两种包装器类型:
ManagedAtomic<T>
泛型类和UnsafeAtomic<T>
泛型结构体。这两种构造都为所有 AtomicValue
类型提供以下操作:
func load(ordering: AtomicLoadOrdering) -> Value
func store(_ desired: Value, ordering: AtomicStoreOrdering)
func exchange(_ desired: Value, ordering: AtomicUpdateOrdering) -> Value
func compareExchange(
expected: Value,
desired: Value,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value)
func compareExchange(
expected: Value,
desired: Value,
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value)
func weakCompareExchange(
expected: Value,
desired: Value,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value)
func weakCompareExchange(
expected: Value,
desired: Value,
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value)
整数类型 带有用于递增或递减值和按位逻辑运算的附加原子操作。
func loadThenWrappingIncrement(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenWrappingDecrement(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenBitwiseAnd(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenBitwiseOr(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenBitwiseXor(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func wrappingIncrementThenLoad(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) -> Value
func wrappingDecrementThenLoad(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) -> Value
func bitwiseAndThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func bitwiseOrThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func bitwiseXorThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func wrappingIncrement(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
)
func wrappingDecrement(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
)
Bool
沿用相同的思路提供选择的其他布尔运算。
func loadThenLogicalAnd(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenLogicalOr(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenLogicalXor(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func logicalAndThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func logicalOrThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func logicalXorThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
有关此软件包提供的 API 的介绍,请参阅 SE-0282 的第一个版本。
当前版本的 Atomics
模块未实现标记原子操作的 API(请参阅 issue #1),但它确实公开了一个 DoubleWord
类型,可用于实现它们。(原子强引用已经根据 DoubleWord
实现,尽管它们当前的形式不公开任何用户可自定义的位。)
Swift Atomics 是一个独立于核心 Swift 项目的库。 我们预计某些原子操作 API 最终可能会被纳入 Swift 标准库。 如果发生这种情况,将使用 Swift 项目已建立的演进过程向 Swift 标准库提出此类更改。
该库在 Swift 许可证下获得许可。 有关更多信息,请参阅 Swift.org 社区准则、贡献准则,以及此存储库根目录下的 LICENSE.txt、CONTRIBUTING.md 和 CODE_OF_CONDUCT.md 文件。
Swift Atomics 使用 GitHub issue 来跟踪错误和增强请求。 我们使用 pull request 进行开发。
我们有一个专门的 Swift Atomics 论坛,人们可以在那里提问和回答关于如何使用或开发此软件包的问题。 这也是讨论其发展的绝佳场所。
如果您发现任何看起来像是错误的东西,请打开一个 Bug 报告! 请尽可能详细地填写。
要修复小问题或进行微小的改进,只需 提交一个 PR,其中包含您想要进行的更改。 如果您正在修复的错误存在 现有问题,请包含对它的引用。 确保添加测试以涵盖您正在进行的所有更改。
对于较大的功能添加,最好在开始开发之前,在新的 功能请求中或在 论坛上讨论您的想法。 如果讨论表明该功能是可取的,请在 PR 中提交实现,并参与其评审讨论。
此软件包定义了大量相似但不完全相同的操作。 为了更容易维护它们,我们使用代码生成来产生它们。
许多 源文件 具有 .swift.gyb
扩展名。 这些文件使用一个基于 Python 的代码生成实用程序,称为 gyb,我们也在 Swift 标准库中使用它(名称是 Generate Your Boilerplate 的缩写)。 为了确保该软件包仍然可以由 SPM 构建,自动生成的输出文件已提交到此存储库中。 您不应编辑 autogenerated
子目录的内容,否则您的更改将在下次重新生成代码时被覆盖。
要重新生成源文件(以及更新 XCTest 测试的清单),您需要手动运行此存储库的 Utilities 文件夹中的脚本 generate-sources.sh
。 每次修改模板文件时都需要执行此操作。
除了 gyb 之外,_AtomicsShims.h
头文件还使用 C 预处理器为每个受支持的原子操作(内存排序配对)定义简单的包装函数。
⚛︎︎