axiom-synchronization

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Mutex & 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

NeedUseReason
Microsecond operationsMutexNo async hop overhead
Protect single propertyMutexSimpler, faster
Complex async workflowsActorProper suspension handling
Suspension points neededActorMutex can't suspend
Shared across modulesMutexSendable, no await needed
High-frequency countersAtomicLock-free performance
需求使用方案原因
微秒级操作Mutex无异步跳转开销
保护单个属性Mutex更简单、更快
复杂异步工作流Actor完善的挂起处理机制
需要挂起点ActorMutex 无法挂起
跨模块共享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
  • Sendable
    — safe to share across concurrency boundaries
  • 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 state
Properties:
  • 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
#endif
swift
#if compiler(>=6.0)
import Synchronization
typealias Lock<T> = Mutex<T>
#else
import os
// 针对 iOS 16-17 使用 OSAllocatedUnfairLock
#endif

Danger: 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
os_unfair_lock
directly in Swift
— it can be moved in memory:
swift
// ❌ 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_lock
——它的内存地址可能会变动:
swift
// ❌ 未定义行为:锁可能被移动
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

内存顺序速查

OrderingReadWriteUse Case
.relaxed
YesYesCounters, no dependencies
.acquiring
Yes-Load before dependent ops
.releasing
-YesStore after dependent ops
.acquiringAndReleasing
YesYesRead-modify-write
.sequentiallyConsistent
YesYesStrongest guarantee
Default choice:
.relaxed
for counters,
.acquiringAndReleasing
for read-modify-write.
顺序读取写入使用场景
.relaxed
计数器、无依赖场景
.acquiring
在依赖操作前加载
.releasing
在依赖操作后存储
.acquiringAndReleasing
读取-修改-写入操作
.sequentiallyConsistent
最强保证
默认选择:计数器使用
.relaxed
,读取-修改-写入操作使用
.acquiringAndReleasing

Resources

参考资源

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