mcpField
A
axiom-synchronization

axiom-synchronization

github.com/charleswiltgen开发1.2k168 次安装今天更新
代码仓库

关于 axiom-synchronization

为 Swift 开发者提供的低层线程同步参考,介绍 Mutex、OSAllocatedUnfairLock 和 Atomic 的选择与用法。内容还说明这些原语与 Swift 并发协作时的风险及常见模式。

主要功能

  1. 01比较 Mutex、锁与 Atomic 的适用场景
  2. 02使用闭包安全访问受保护状态
  3. 03构建线程安全计数器与值包装器
  4. 04为 iOS 16 提供锁实现回退
  5. 05避免锁跨越 await 和阻塞线程池

适用场景

  1. 01保护高频读取的单个属性
  2. 02实现无锁计数器或标志位
  3. 03在 Actor 中提供同步快速读取

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-synchronization

安装后在 Claude Code 中输入/axiom-synchronization确认已加载。

详细介绍

互斥锁与同步 — 线程安全原语

当 Actor 速度太慢或开销太大时,可使用底层同步原语。

何时使用 Mutex,何时使用 Actor

需求使用原因
微秒级操作Mutex没有异步跳转开销
保护单个属性Mutex更简单、更快
复杂的异步工作流Actor能妥善处理挂起
需要挂起点ActorMutex 无法挂起
在多个模块间共享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