在 iOS 开发中,处理 “资源竞争”(多线程并发访问共享资源)时,加锁是保证线程安全的核心手段,但不当的锁使用会导致性能损耗、死锁等问题。
iOS 资源竞争的加锁优化核心是:“合适的锁 + 最小化范围 + 避免死锁 + 无锁优先”。
一、各种锁的性能对比(版本 & 特性)
| 锁类型 | iOS 版本 | 性能 | 场景 | 是否推荐 |
|---|---|---|---|---|
@synchronized | iOS 2.0+ | 低 | OC语法糖,是递归锁 | ⚠️ 谨慎,性能差 |
NSRecursiveLock | iOS 2.0+ | 低 | 递归调用场景 | ⚠️ 谨慎,性能差 |
pthread_mutex | iOS 2.0+ | 中等 | 兼容性最强,跨平台 | ✅ 老代码/底层封装 |
NSLock | iOS 2.0+ | 中等 | 面向对象简单使用 | ✅ 小项目常用 |
dispatch_semaphore | iOS 4.0+ | 中等 | GCD 任务同步 | ✅ 合理使用 |
os_unfair_lock | iOS 10.0+ | 高 | 高性能场景 | ✅ 推荐 (C API) |
Swift 5.5 Actor | iOS 15.0+ | 高 | 基于隔离机制的 “无锁” | ✅ 强烈推荐 |
OSAllocatedUnfairLock | iOS 16.0+ | 高 | Swift 项目安全锁 | ✅ 强烈推荐 |
Swift 6 Mutex | iOS 18.0+ | 高 | 现代 Swift,未来趋势 | ✅ 新项目/新系统 |
二、正确的使用建议(按 iOS 版本分层)
-
iOS 10 以下
- 推荐:
pthread_mutex或NSLock - 说明:兼容性优先,性能一般。
- 推荐:
-
iOS 10 ~ 15
- 推荐:
os_unfair_lock(性能最优)。 - Swift 项目中用时要小心释放问题(结构体值类型)。
- 推荐:
-
iOS 16 ~ 17
- 推荐:
OSAllocatedUnfairLock或Actor。 - 说明:Apple 推荐在 Swift 中用,系统封装,简易好用。
- 推荐:
-
iOS 18+
- 推荐:Swift 6
Mutex。 - 说明:语法现代、安全、易用,属于未来趋势。
- 推荐:Swift 6
三、加锁原则
最小化锁的持有范围(减少阻塞)
锁的性能损耗主要来自 “线程等待”,缩短锁的持有时间是关键优化手段。
避免死锁(规范锁的使用顺序)
死锁常因 “循环等待锁” 导致,例如线程 A 持有锁 1 等待锁 2,线程 B 持有锁 2 等待锁 1。
避免嵌套锁
尽量用单锁解决问题,嵌套锁会增加死锁风险(除非用递归锁,但递归锁开销更高)。
用 “无锁” 机制替代显式加锁
原子操作(针对简单值)
对 Int、Bool 等简单类型,用 std::atomic(C++)或 Swift 原子属性(iOS 17+),比锁更高效。
Swift Actor(推荐)
Actor 是 Swift 5.5 引入的并发模型,通过 “隔离域” 保证状态只能被自身线程访问,外部通过 await 异步调用,避免显式加锁
四、根据系统版本,封装自适应最优锁实现
- iOS 18+:用 Swift 6 的
Mutex(Synchronization 框架)。 - iOS 16 ~ 17:用
OSAllocatedUnfairLock。 - iOS 10 ~ 15:用
os_unfair_lock。 - iOS 9 及以下:用
pthread_mutex。
import Foundation
import os
#if canImport(Synchronization)
import Synchronization
#endif
/// 跨版本最优锁实现 iOS10+(支持递归锁)
/// - 非递归场景:自动选择性能最优锁(Swift Mutex > OSAllocatedUnfairLock > os_unfair_lock)
/// - 递归场景:使用 NSRecursiveLock(Foundation 原生,更安全)
/// - 推荐通过 `withLock` 方法使用,自动管理锁生命周期
final class OptimalLock {
private let impl: Locking
/// 初始化锁
/// - Parameter recursive: 是否需要递归锁(同一线程可重复加锁)
init(recursive: Bool = false) {
if recursive {
// 递归场景
impl = RecursiveLockWrapper()
} else {
// 非递归场景:按系统版本选择高性能锁
if #available(iOS 18, *) {
impl = MutexLock()
} else if #available(iOS 16, *) {
impl = OSAllocatedUnfairLockWrapper()
} else {
impl = UnfairLockWrapper()
}
}
}
/// 执行临界区代码(自动加锁/解锁)
/// 在抛出错误前会统一打印错误信息
@discardableResult
func withLock<T>(_ block: () throws -> T) rethrows -> T {
do {
return try impl.withLock(block)
} catch {
// 统一打印错误信息
DebugToast.showFailed(text:"OptimalLock 临界区执行错误: (error)")
// 打印错误堆栈信息(可选,根据需要开启)
debugPrint("错误堆栈: (Thread.callStackSymbols)")
// 重新抛出错误,让调用者可以继续处理
throw error
}
}
}
// MARK: - 核心协议定义
private protocol Locking {
func withLock<T>(_ block: () throws -> T) rethrows -> T
}
// MARK: - 非递归锁实现(性能优先)
@available(iOS 18, *)
private final class MutexLock: Locking {
private let mutex = Mutex(())
func withLock<T>(_ block: () throws -> T) rethrows -> T {
try mutex.withLock { _ in try block() }
}
}
@available(iOS 16, *)
private final class OSAllocatedUnfairLockWrapper: Locking {
private let lock = OSAllocatedUnfairLock()
func withLock<T>(_ block: () throws -> T) rethrows -> T {
lock.lock()
defer { lock.unlock() }
return try block()
}
}
@available(iOS 10, *)
private final class UnfairLockWrapper: Locking {
private var lock = os_unfair_lock()
func withLock<T>(_ block: () throws -> T) rethrows -> T {
os_unfair_lock_lock(&lock)
defer { os_unfair_lock_unlock(&lock) }
return try block()
}
}
// MARK: - 递归锁实现(安全优先)
/// 封装 NSRecursiveLock(Foundation 原生递归锁,推荐用于递归场景)
@available(iOS 2, *)
private final class RecursiveLockWrapper: Locking {
private let lock = NSRecursiveLock()
func withLock<T>(_ block: () throws -> T) rethrows -> T {
lock.lock()
defer { lock.unlock() }
return try block()
}
}
extension OptimalLock {
/// 带超时的临界区执行(仅递归锁有效),应对可能的死锁场景
func withLock<T>(timeout: TimeInterval, _ block: () throws -> T) rethrows -> T? {
guard let recursiveLock = impl as? RecursiveLockWrapper else {
DebugToast.showFailed(text: "withLock(timeout:) 仅支持递归锁,请使用 OptimalLock(recursive: true) 初始化")
return nil
}
return try recursiveLock.withLock(timeout: timeout, block)
}
}
private extension RecursiveLockWrapper {
func withLock<T>(timeout: TimeInterval, _ block: () throws -> T) rethrows -> T? {
let deadline = Date().addingTimeInterval(timeout)
guard lock.lock(before: deadline) else {
DebugToast.showFailed(text: "递归锁调用超时")
return nil
}
defer { lock.unlock() }
return try block()
}
}
let lock = OptimalLock()
var shared = 0
DispatchQueue.concurrentPerform(iterations: 100) { _ in
lock.withLock {
shared += 1
}
}
print("结果: (shared)") // 结果: 100
封装特点:
- 统一 API:对外始终是
withLock { }。 - 最优选择:不同 iOS 版本自动切换最佳锁实现。
- 错误处理:极端情况,若加锁失败会抛出错误,进行统一提示处理。
五、工具辅助:检测和优化锁性能
Thread Sanitizer(TSAN)
Xcode 内置工具,可检测代码中的 “数据竞争”(未加锁的共享资源访问),在 Debug 模式下启用:
Product > Scheme > Edit Scheme > Run > Diagnostics > Thread Sanitizer。
Instruments(Time Profiler + Locks)用 “Locks” 模板分析锁的争用情况
- 查看
os_unfair_lock_lock等函数的耗时,定位频繁阻塞的锁。 - 统计锁的等待次数,优化高频争用的锁(如替换为更轻量的同步方式)。
性能测试(Benchmark)
对不同锁的性能进行量化对比(如用 XCTest 测量每秒操作次数),选择最适合当前场景的锁。
六、iOS 锁机制的演进历史
pthread_mutex (iOS 2.0+)
- 历史:POSIX 标准,C 层提供,最早就有。
- 特性:通用、跨平台,支持递归锁/条件变量等。
- 缺点:用法偏底层,封装成本高,API 不够 Swift 风格。
NSLock / NSRecursiveLock (iOS 2.0+)
- 历史:Foundation 封装的锁。
- 特性:面向对象,API 简单,支持递归(
NSRecursiveLock)。 - 缺点:性能一般,比 GCD 或 unfair lock 慢。
dispatch_semaphore / DispatchQueue (iOS 4.0+)
- 历史:随 GCD 引入。
- 特性:信号量值设为 1 时可充当互斥锁;串行队列也能避免资源竞争。
- 缺点:语义上不直观;信号量误用容易死锁。
os_unfair_lock (iOS 10.0+)
- 历史:Apple 官方替代
OSSpinLock。 - 特性:低开销锁,避免了优先级反转;性能最佳。
- 缺点:是 C struct,值类型,Swift 使用要小心(释放时可能 UB)。
OSAllocatedUnfairLock (iOS 16.0+)
- 历史:Swift 时代的封装,解决
os_unfair_lock的不安全问题。 - 特性:安全封装成类,内存管理正确;推荐在 Swift 中用。
- 缺点:iOS 16+ 才可用,低版本无法兼容。
Swift 6 Mutex (iOS 18.0+)
- 历史:Swift 6 引入 Synchronization 框架。
- 特性:官方 Swift 原生 API,易用、安全、现代化;支持
lock { … }作用域语法。 - 缺点:⚠️ iOS 18 才能用,对兼容性要求高。