axiom-synchronization
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMutex & Synchronization — Thread-Safe Primitives
Mutex 与同步 —— 线程安全原语
Low-level synchronization primitives for when actors are too slow or heavyweight.
当 Actor 过于缓慢或重量级时使用的底层同步原语。
When to Use Mutex vs Actor
何时使用 Mutex 而非 Actor
| Need | Use | Reason |
|---|---|---|
| Microsecond operations | Mutex | No async hop overhead |
| Protect single property | Mutex | Simpler, faster |
| Complex async workflows | Actor | Proper suspension handling |
| Suspension points needed | Actor | Mutex can't suspend |
| Shared across modules | Mutex | Sendable, no await needed |
| High-frequency counters | Atomic | Lock-free performance |
| 需求 | 使用方案 | 原因 |
|---|---|---|
| 微秒级操作 | Mutex | 无异步跳转开销 |
| 保护单个属性 | Mutex | 更简单、更快 |
| 复杂异步工作流 | Actor | 完善的挂起处理机制 |
| 需要挂起点 | Actor | Mutex 无法挂起 |
| 跨模块共享 | Mutex | 符合 Sendable,无需 await |
| 高频计数器 | Atomic | 无锁高性能 |
API Reference
API 参考
Mutex (iOS 18+ / Swift 6)
Mutex(iOS 18+ / Swift 6)
swift
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
}Properties:
- Generic over protected value
- — safe to share across concurrency boundaries
Sendable - Closure-based access only (no lock/unlock methods)
swift
import Synchronization
let mutex = Mutex<Int>(0)
// 读取
let value = mutex.withLock { $0 }
// 写入
mutex.withLock { $0 += 1 }
// 非阻塞尝试
if let value = mutex.withLockIfAvailable({ $0 }) {
// 获取到锁
}特性:
- 对受保护值支持泛型
- —— 可安全跨并发边界共享
Sendable - 仅支持基于闭包的访问(无单独的加锁/解锁方法)
OSAllocatedUnfairLock (iOS 16+)
OSAllocatedUnfairLock(iOS 16+)
swift
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 stateProperties:
- Heap-allocated, stable memory address
- Non-recursive (can't re-lock from same thread)
Sendable
swift
import os
let lock = OSAllocatedUnfairLock(initialState: 0)
// 基于闭包(推荐)
lock.withLock { state in
state += 1
}
// 传统方式(仅同线程可用)
lock.lock()
defer { lock.unlock() }
// 访问受保护状态特性:
- 堆分配,内存地址稳定
- 非递归(无法在同一线程重复加锁)
Sendable
Atomic Types (iOS 18+)
Atomic 类型(iOS 18+)
swift
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
)swift
import Synchronization
let counter = Atomic<Int>(0)
// 原子自增
counter.wrappingAdd(1, ordering: .relaxed)
// 比较并交换
let (exchanged, original) = counter.compareExchange(
expected: 0,
desired: 42,
ordering: .acquiringAndReleasing
)Patterns
模式
Pattern 1: Thread-Safe Counter
模式 1:线程安全计数器
swift
final class Counter: Sendable {
private let mutex = Mutex<Int>(0)
var value: Int { mutex.withLock { $0 } }
func increment() { mutex.withLock { $0 += 1 } }
}swift
final class Counter: Sendable {
private let mutex = Mutex<Int>(0)
var value: Int { mutex.withLock { $0 } }
func increment() { mutex.withLock { $0 += 1 } }
}Pattern 2: Sendable Wrapper
模式 2:Sendable 包装器
swift
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 } }
}
}swift
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 } }
}
}Pattern 3: Fast Sync Access in Actor
模式 3:Actor 中的快速同步访问
swift
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 }
}
}swift
actor ImageCache {
// 用于无需 Actor 跳转的快速同步读取的 Mutex
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 }
}
}Pattern 4: Lock-Free Counter with Atomic
模式 4:基于 Atomic 的无锁计数器
swift
final class FastCounter: Sendable {
private let _value = Atomic<Int>(0)
var value: Int { _value.load(ordering: .relaxed) }
func increment() {
_value.wrappingAdd(1, ordering: .relaxed)
}
}swift
final class FastCounter: Sendable {
private let _value = Atomic<Int>(0)
var value: Int { _value.load(ordering: .relaxed) }
func increment() {
_value.wrappingAdd(1, ordering: .relaxed)
}
}Pattern 5: iOS 16 Fallback
模式 5:iOS 16 兼容方案
swift
#if compiler(>=6.0)
import Synchronization
typealias Lock<T> = Mutex<T>
#else
import os
// Use OSAllocatedUnfairLock for iOS 16-17
#endifswift
#if compiler(>=6.0)
import Synchronization
typealias Lock<T> = Mutex<T>
#else
import os
// 针对 iOS 16-17 使用 OSAllocatedUnfairLock
#endifDanger: Mixing with Swift Concurrency
风险:与 Swift Concurrency 混用
Never Hold Locks Across Await
绝不要在 Await 期间持有锁
swift
// ❌ 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 }swift
// ❌ 存在死锁风险
mutex.withLock {
await someAsyncWork() // 任务挂起时仍持有锁!
}
// ✅ 安全做法:在 await 前释放锁
let value = mutex.withLock { $0 }
let result = await process(value)
mutex.withLock { $0 = result }Why Semaphores/RWLocks Are Unsafe
为什么信号量/读写锁不安全
Swift's cooperative thread pool has limited threads. Blocking primitives exhaust the pool:
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() }
}Swift 的协作线程池线程数量有限。阻塞型原语会耗尽线程池:
swift
// ❌ 危险:阻塞协作线程
let semaphore = DispatchSemaphore(value: 0)
Task {
semaphore.wait() // 线程被阻塞,无法运行其他任务!
}
// ✅ 改用异步延续
await withCheckedContinuation { continuation in
// 非阻塞回调
callback { continuation.resume() }
}os_unfair_lock Danger
os_unfair_lock 的风险
Never use directly in Swift — it can be moved in memory:
os_unfair_lockswift
// ❌ 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()绝不要在 Swift 中直接使用 ——它的内存地址可能会变动:
os_unfair_lockswift
// ❌ 未定义行为:锁可能被移动
var lock = os_unfair_lock()
os_unfair_lock_lock(&lock) // 地址可能无效
// ✅ 使用 OSAllocatedUnfairLock(堆分配,地址稳定)
let lock = OSAllocatedUnfairLock()Decision Tree
决策树
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)需要同步处理?
├─ 是否需要无锁操作?
│ └─ 简单计数器/标志? → Atomic
│ └─ 复杂状态? → Mutex
├─ 是否支持 iOS 18+?
│ └─ 是 → Mutex
│ └─ 否,支持 iOS 16+? → OSAllocatedUnfairLock
├─ 是否需要挂起点?
│ └─ 是 → Actor(而非锁)
├─ 是否需要跨 await 访问?
│ └─ 是 → Actor(而非锁)
└─ 是否为性能关键的热点路径?
└─ 是 → Mutex/Atomic(而非 Actor)Common Mistakes
常见错误
Mistake 1: Using Lock for Async Coordination
错误 1:使用锁进行异步协调
swift
// ❌ 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 }
}swift
// ❌ 锁无法用于异步场景
let mutex = Mutex<Bool>(false)
Task {
await someWork()
mutex.withLock { $0 = true } // 仍可能出现竞态条件
}
// ✅ 使用 Actor 或异步状态
actor AsyncState {
var isComplete = false
func complete() { isComplete = true }
}Mistake 2: Recursive Locking Attempt
错误 2:尝试递归加锁
swift
// ❌ 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)swift
// ❌ 死锁 —— OSAllocatedUnfairLock 不支持递归
lock.withLock {
doWork() // 如果 doWork() 也调用 withLock → 死锁
}
// ✅ 重构以避免嵌套加锁
let data = lock.withLock { $0.copy() }
doWork(with: data)Mistake 3: Mixing Lock Styles
错误 3:混合使用不同锁风格
swift
// ❌ Don't mix lock/unlock with withLock
lock.lock()
lock.withLock { /* ... */ } // Deadlock!
lock.unlock()
// ✅ Pick one style
lock.withLock { /* all work here */ }swift
// ❌ 不要混合使用 lock/unlock 和 withLock
lock.lock()
lock.withLock { /* ... */ } // 死锁!
lock.unlock()
// ✅ 选择一种风格
lock.withLock { /* 所有操作在此完成 */ }Memory Ordering Quick Reference
内存顺序速查
| Ordering | Read | Write | Use Case |
|---|---|---|---|
| Yes | Yes | Counters, no dependencies |
| Yes | - | Load before dependent ops |
| - | Yes | Store after dependent ops |
| Yes | Yes | Read-modify-write |
| Yes | Yes | Strongest guarantee |
Default choice: for counters, for read-modify-write.
.relaxed.acquiringAndReleasing| 顺序 | 读取 | 写入 | 使用场景 |
|---|---|---|---|
| 是 | 是 | 计数器、无依赖场景 |
| 是 | 否 | 在依赖操作前加载 |
| 否 | 是 | 在依赖操作后存储 |
| 是 | 是 | 读取-修改-写入操作 |
| 是 | 是 | 最强保证 |
默认选择:计数器使用 ,读取-修改-写入操作使用 。
.relaxed.acquiringAndReleasingResources
参考资源
Docs: /synchronization, /synchronization/mutex, /os/osallocatedunfairlock
Swift Evolution: SE-0433
Skills: axiom-swift-concurrency, axiom-swift-performance
文档:/synchronization, /synchronization/mutex, /os/osallocatedunfairlock
Swift 演进提案:SE-0433
技能:axiom-swift-concurrency, axiom-swift-performance