Swift/Objective-C互斥锁NSLock
Swift/Objective-C互斥锁NSLock

Swift/Objective-C互斥锁NSLock

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/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注