NSLock 是一种互斥锁(mutex),用于在多线程环境中保护共享资源,防止多个线程同时访问同一块数据而导致数据竞争、崩溃或错误结果。
基本用法
let lock = NSLock()
lock.lock()
// 临界区:操作共享资源的代码
lock.unlock()
调用 lock() 会阻塞当前线程直到锁被释放,而 unlock() 会释放锁,允许其他线程进入。
代码示例:线程安全的计数器
class Counter {
private var value = 0
private let lock = NSLock()
func increment() {
lock.lock()
value += 1
lock.unlock()
}
func getValue() -> Int {
lock.lock()
defer { lock.unlock() } // 确保退出前释放锁
return value
}
}
在多个线程中调用 increment() 时,NSLock 保证同一时间只有一个线程能访问 value。
如果没有加锁会怎样?
class UnsafeCounter {
var value = 0
func increment() {
value += 1 // 多线程时会出现“丢失写入”或崩溃
}
}
在多线程中访问 value 会发生 数据竞争(race condition),可能导致:
值不正确(比如两个线程同时增加,实际只加了 1)。
崩溃(访问冲突)。
高级用法
1、locak():阻塞直到获取锁。
2、unlock():释放锁。
3、try():尝试获取锁,若失败立即返回false,不会阻塞。
if lock.try() {
// 成功加锁
lock.unlock()
} else {
// 没有加锁成功,立即返回
}
4、locak(befor:):设定时间内尝试获取锁,失败后返回false。
let lock = NSLock()
let timeout = Date().addingTimeInterval(2) // 最多等待 2 秒
if lock.lock(before: timeout) {
print("成功获得锁")
// 进行线程安全操作
lock.unlock()
} else {
print("等待锁失败,超时")
}
适合尝试性抢锁,后台线程并发操作中避免死锁,对共享资源设置合理等待时限。
注意事项
每个 lock() 必须对应一个 unlock(),否则会造成死锁(deadlock)。
尽量使用 defer 保证锁一定会释放:
lock.lock()
defer { lock.unlock() }
锁住的代码要尽量精简,不要执行耗时操作(避免长时间占用锁)。
替换方案:Actor
Swift 并发模型推荐用 actor 自动管理线程安全:
actor SafeCounter {
private var value = 0
func increment() {
value += 1
}
func getValue() -> Int {
value
}
}
比 NSLock 更安全、简洁。
相关文章
1、Swift Concurrency并发:https://fangjunyu.com/2025/05/15/swift-concurrency%e5%b9%b6%e5%8f%91/
2、Swift并发模型中的Actor:https://fangjunyu.com/2024/10/22/swift%e7%a7%91%e6%99%ae%e6%96%87%e3%80%8aactor-%e9%9a%94%e7%a6%bb%e3%80%8b/