关于 axiom-synchronization
为 Swift 开发者提供的低层线程同步参考,介绍 Mutex、OSAllocatedUnfairLock 和 Atomic 的选择与用法。内容还说明这些原语与 Swift 并发协作时的风险及常见模式。
主要功能
- 01比较 Mutex、锁与 Atomic 的适用场景
- 02使用闭包安全访问受保护状态
- 03构建线程安全计数器与值包装器
- 04为 iOS 16 提供锁实现回退
- 05避免锁跨越 await 和阻塞线程池
适用场景
- 01保护高频读取的单个属性
- 02实现无锁计数器或标志位
- 03在 Actor 中提供同步快速读取
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-synchronization安装后在 Claude Code 中输入/axiom-synchronization确认已加载。
详细介绍
互斥锁与同步 — 线程安全原语
当 Actor 速度太慢或开销太大时,可使用底层同步原语。
何时使用 Mutex,何时使用 Actor
| 需求 | 使用 | 原因 |
|---|---|---|
| 微秒级操作 | Mutex | 没有异步跳转开销 |
| 保护单个属性 | Mutex | 更简单、更快 |
| 复杂的异步工作流 | Actor | 能妥善处理挂起 |
| 需要挂起点 | Actor | Mutex 无法挂起 |
| 在多个模块间共享 | Mutex | 可安全发送,无需 await |
| 高频计数器 | Atomic | 无锁性能 |
API 参考
Mutex(iOS 18+ / Swift 6)
import Synchronization
let mutex = Mutex<Int>(0)
// Read
let value = mutex.withLock { $0 }
// Write
mutex.withLock { $0 += 1 }
// Non-blocking attempt
if let value = mutex.withLockIfAvailable({ $0 }) {
// Got the lock
}
属性:
- 对受保护值进行泛型封装
Sendable— 可安全地跨并发边界共享- 仅通过闭包访问(没有 lock/unlock 方法)
OSAllocatedUnfairLock(iOS 16+)
import os
let lock = OSAllocatedUnfairLock(initialState: 0)
// Closure-based (recommended)
lock.withLock { state in
state += 1
}
// Traditional (same-thread only)
lock.lock()
defer { lock.unlock() }
// access protected state
属性:
- 在堆上分配,内存地址稳定
- 非递归(同一线程无法重复加锁)
Sendable
Atomic 类型(iOS 18+)
import Synchronization
let counter = Atomic<Int>(0)
// Atomic increment
counter.wrappingAdd(1, ordering: .relaxed)
// Compare-and-swap
let (exchanged, original) = counter.compareExchange(
expected: 0,
desired: 42,
ordering: .acquiringAndReleasing
)
模式
模式 1:线程安全计数器
final class Counter: Sendable {
private let mutex = Mutex<Int>(0)
var value: Int { mutex.withLock { $0 } }
func increment() { mutex.withLock { $0 += 1 } }
}
模式 2:Sendable 包装器
final class ThreadSafeValue<T: Sendable>: @unchecked Sendable {
private let mutex: Mutex<T>
init(_ value: T) { mutex = Mutex(value) }
var value: T {
get { mutex.withLock { $0 } }
set { mutex.withLock { $0 = newValue } }
}
}
模式 3:Actor 中的快速同步访问
actor ImageCache {
// Mutex for fast sync reads without actor hop
private let mutex = Mutex<[URL: Data]>([:])
nonisolated func cachedSync(_ url: URL) -> Data? {
mutex.withLock { $0[url] }
}
func cacheAsync(_ url: URL, data: Data) {
mutex.withLock { $0[url] = data }
}
}
模式 4:使用 Atomic 的无锁计数器
final class FastCounter: Sendable {
private let _value = Atomic<Int>(0)
var value: Int { _value.load(ordering: .relaxed) }
func increment() {
_value.wrappingAdd(1, ordering: .relaxed)
}
}
模式 5:iOS 16 回退方案
#if compiler(>=6.0)
import Synchronization
typealias Lock<T> = Mutex<T>
#else
import os
// Use OSAllocatedUnfairLock for iOS 16-17
#endif
危险:与 Swift 并发混用
绝不要在 await 期间持有锁
// ❌ DEADLOCK RISK
mutex.withLock {
await someAsyncWork() // Task suspends while holding lock!
}
// ✅ SAFE: Release before await
let value = mutex.withLock { $0 }
let result = await process(value)
mutex.withLock { $0 = result }
信号量/RWLock 为何不安全
Swift 的协作式线程池线程数有限。阻塞原语会耗尽线程池:
// ❌ DANGEROUS: Blocks cooperative thread
let semaphore = DispatchSemaphore(value: 0)
Task {
semaphore.wait() // Thread blocked, can't run other tasks!
}
// ✅ Use async continuation instead
await withCheckedContinuation { continuation in
// Non-blocking callback
callback { continuation.resume() }
}
os_unfair_lock 的危险
绝不要在 Swift 中直接使用 os_unfair_lock — 它在内存中的位置可能发生变化:
// ❌ UNDEFINED BEHAVIOR: Lock may move
var lock = os_unfair_lock()
os_unfair_lock_lock(&lock) // Address may be invalid
// ✅ Use OSAllocatedUnfairLock (heap-allocated, stable address)
let lock = OSAllocatedUnfairLock()
决策树
Need synchronization?
├─ Lock-free operation needed?
│ └─ Simple counter/flag? → Atomic
│ └─ Complex state? → Mutex
├─ iOS 18+ available?
│ └─ Yes → Mutex
│ └─ No, iOS 16+? → OSAllocatedUnfairLock
├─ Need suspension points?
│ └─ Yes → Actor (not lock)
├─ Cross-await access?
│ └─ Yes → Actor (not lock)
└─ Performance-critical hot path?
└─ Yes → Mutex/Atomic (not actor)
常见错误
错误 1:使用锁进行异步协调
// ❌ Locks don't work with async
let mutex = Mutex<Bool>(false)
Task {
await someWork()
mutex.withLock { $0 = true } // Race condition still possible
}
// ✅ Use actor or async state
actor AsyncState {
var isComplete = false
func complete() { isComplete = true }
}
错误 2:尝试递归加锁
// ❌ Deadlock — OSAllocatedUnfairLock is non-recursive
lock.withLock {
doWork() // If doWork() also calls withLock → deadlock
}
// ✅ Refactor to avoid nested locking
let data = lock.withLock { $0.copy() }
doWork(with: data)
错误 3:混用锁的使用方式
// ❌ Don't mix lock/unlock with withLock
lock.lock()
lock.withLock { /* ... */ } // Deadlock!
lock.unlock()
// ✅ Pick one style
lock.withLock { /* all work here */ }
内存顺序速查
| 顺序 | 读取 | 写入 | 使用场景 |
|---|---|---|---|
.relaxed | 是 | 是 | 计数器,无依赖关系 |
.acquiring | 是 | - | 在依赖操作前加载 |
.releasing | - | 是 | 在依赖操作后存储 |
.acquiringAndReleasing | 是 | 是 | 读-改-写 |
.sequentiallyConsistent | 是 | 是 | 最强保证 |
默认选择:计数器使用 .relaxed,读-改-写使用 .acquiringAndReleasing。
资源
文档:/synchronization、/synchronization/mutex、/os/osallocatedunfairlock
Swift 演进提案:SE-0433
技能:axiom-swift-concurrency、axiom-swift-performance
评论
支持 Markdown