NSLock 详解

NSLock 是 Objective-C 提供的一种 轻量级互斥锁,用于保证多线程访问共享资源的安全性。相比 @synchronized,它的性能更好,并且提供了更灵活的锁管理方法。

1. NSLock 的基本使用

1)lock和unlock

@interface SafeCounter : NSObject
@property (nonatomic, strong) NSLock *lock;
@property (nonatomic, assign) NSInteger count;
@end

@implementation SafeCounter

- (instancetype)init {
    if (self = [super init]) {
        _lock = [[NSLock alloc] init];
    }
    return self;
}

- (void)increment {
    [self.lock lock]; // 加锁
    self.count += 1;
    NSLog(@"当前 count: %ld", (long)self.count);
    [self.lock unlock]; // 解锁
}

@end

lock 确保 count 在多个线程访问时不会竞争,避免数据错乱。lock 后必须 unlock,否则会导致死锁。

2)我们还可以使用tryLock(尝试加锁,不阻塞),如果 tryLock 获取到锁,返回 YES,否则返回 NO,不会阻塞线程:

if ([self.lock tryLock]) {
    // 线程安全代码
    [self.lock unlock];
} else {
    NSLog(@"锁已被占用,执行其他逻辑");
}

适用于低优先级任务,避免等待锁导致线程卡住。

3)lockBeforeDate:(限时加锁)

设置超时时间,超过这个时间还拿不到锁,就放弃:

if ([self.lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:2]]) {
    // 线程安全代码
    [self.lock unlock];
} else {
    NSLog(@"超时未获取到锁");
}

适用于有时间敏感性的任务,防止线程长时间等待锁。

2. NSLock 能解决哪些问题?

  1. 防止数据竞争

    • 例如多个线程同时读写 NSMutableArrayNSMutableDictionary 时可能崩溃,NSLock 可以确保一个线程完成操作后,另一个线程才能访问。
  2. 避免死锁

    • NSLock 提供 tryLocklockBeforeDate: 方法,可以防止线程长时间等待锁,降低死锁的风险。
  3. 提高 @synchronized 的性能

    • @synchronized 内部也是基于 NSLock 实现的,但多了一些管理开销,因此 NSLock 更轻量,性能更高。

3、注意事项

避免多个线程交叉锁(死锁)

如果一个线程持有 lock1,等待 lock2,而另一个线程持有 lock2,等待 lock1,就会产生死锁:

[self.lock1 lock];
[self.lock2 lock]; // 这里可能导致死锁
[self.lock2 unlock];
[self.lock1 unlock];

正确做法

  • 尽量保持加锁顺序一致,避免交叉等待。
  • 考虑用 tryLock 失败时执行其他逻辑。

总结

  1. NSLock@synchronized 的更轻量级版本,适合需要手动管理锁的场景。
  2. 支持 tryLocklockBeforeDate:,可以避免死锁,提高程序的健壮性。
  3. 必须保证 lock 之后一定 unlock,避免死锁或线程卡死。
  4. 适用于高频加锁解锁场景,但不支持递归调用(可用 NSRecursiveLock 代替)。

你可能感兴趣的:(ios)