跳到主要内容

线程安全

问题

iOS 中有哪些保证线程安全的方式?各种锁的性能和适用场景?

答案

常见线程安全方案

方案类型性能特点
os_unfair_lock自旋→互斥⭐⭐⭐⭐⭐iOS 10+,最快的锁
pthread_mutex互斥锁⭐⭐⭐⭐POSIX 标准
NSLock互斥锁⭐⭐⭐ObjC 封装
NSRecursiveLock递归锁⭐⭐⭐同线程可重入
DispatchQueue(serial)串行队列⭐⭐⭐GCD 串行化
dispatch_barrier屏障⭐⭐⭐读写锁效果
ActorSwift 并发⭐⭐⭐⭐编译器保证

串行队列实现线程安全

class ThreadSafeArray<T> {
private var array: [T] = []
private let queue = DispatchQueue(label: "com.app.threadSafeArray")

func append(_ element: T) {
queue.async { self.array.append(element) }
}

func getAll() -> [T] {
queue.sync { return array }
}
}

读写锁(dispatch_barrier)

class ReadWriteCache<Key: Hashable, Value> {
private var dict: [Key: Value] = [:]
private let queue = DispatchQueue(label: "com.app.cache", attributes: .concurrent)

func read(key: Key) -> Value? {
queue.sync { dict[key] } // 读:并发执行
}

func write(key: Key, value: Value) {
queue.async(flags: .barrier) { // 写:独占执行
self.dict[key] = value
}
}
}

NSLock

class Counter {
private var count = 0
private let lock = NSLock()

func increment() {
lock.lock()
defer { lock.unlock() }
count += 1
}
}
死锁风险

NSLock 不支持递归加锁——如果在同一线程上重复 lock 会死锁。需要递归锁时使用 NSRecursiveLock

@Sendable 与线程安全检查

// Swift 6 严格并发检查
func process(_ data: @Sendable () -> Void) {
Task { data() }
}

// 非 Sendable 类型不能跨并发域传递
class MutableState { // ⚠️ 编译器警告
var value = 0
}

常见面试问题

Q1: OSSpinLock 为什么被废弃?

答案:自旋锁存在优先级反转问题——低优先级线程持有锁时,高优先级线程自旋等待,CPU 一直调度高优先级线程,低优先级线程得不到执行机会,无法释放锁。os_unfair_lock 替代了它,会在等待时让出 CPU。

Q2: atomic 和线程安全的关系?

答案:ObjC 的 atomic 属性保证 getter/setter 是原子操作(不会读到写一半的值),但不保证线程安全。例如 self.array 是 atomic,但 [self.array addObject:] 不是原子操作——读取和修改之间可能被其他线程插入操作。

Q3: Swift 有 atomic 属性吗?

答案:Swift 没有内置的 atomic 属性关键字。线程安全需要手动使用锁、队列或 Actor。从 Swift 6 开始,编译器通过 Sendable 检查在编译时发现潜在的数据竞争。

相关链接