NSLock
是iOS
开发中最基础的锁。它继承自NSObject
,遵守NSLocking
协议。用于处理线程安全问题。
下面我们来看一个例子:
for (int i = 0; i < 200000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.mArray = [NSMutableArray array];
});
}
运行该程序会崩溃,这是因为,我们在不断地创建array
,mArray
在不断的赋新值,释放旧值,这个时候多线程操作就会可能存在值已经被释放了,而其他线程还在操作,此时就会发生崩溃。此时就需要我们对程序加锁。将上述程序改成如下:
NSLock *lock = [[NSLock alloc] init];
for (int i = 0; i < 200000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[lock lock];
self.mArray = [NSMutableArray array];
[lock unlock];
});
}
程序就能正常运行了,这是因为此时,每一条线程执行self.mArray = [NSMutableArray array]
的前后,都会有获取锁释放锁的过程,此时这句代码是在线程安全的情况下执行的,所以并没有异常问题。
那么NSLock
到底做了什么?附上一份Objective-C
的源码:
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name;
@end
以上方法只能看到定义,并不能看到实现,那我们再通过swift
的开源代码来看看:
open class NSLock: NSObject, NSLocking {
internal var mutex = _MutexPointer.allocate(capacity: 1)
private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
public override init() {
pthread_mutex_init(mutex, nil)
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
}
deinit {
pthread_mutex_destroy(mutex)
mutex.deinitialize(count: 1)
mutex.deallocate()
deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
}
// 获取锁
open func lock() {
pthread_mutex_lock(mutex)
}
// 释放锁
open func unlock() {
pthread_mutex_unlock(mutex)
// Wakeup any threads waiting in lock(before:)
pthread_mutex_lock(timeoutMutex)
pthread_cond_broadcast(timeoutCond)
pthread_mutex_unlock(timeoutMutex)
}
// 尝试加锁,不会阻塞线程。true则加锁成功。
// false则失败,说明其他线程在加锁中这个方法无论如何都会立即返回。
open func `try`() -> Bool {
return pthread_mutex_trylock(mutex) == 0
}
// 尝试在指定NSDate之前加锁,会阻塞线程。true则加锁成功。
// false则失败,说明其他线程在加锁中这个方法无论如何都会立即返回。
open func lock(before limit: Date) -> Bool {
if pthread_mutex_trylock(mutex) == 0 {
return true
}
return timedLock(mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
}
open var name: String?
}
从源码上看NSLock
其实只是对pthread_mutex
做了一层简单的封装。它属于互斥锁的一种。当一个线程进行访问的时候,该线程获得锁,其他线程进行访问的时候,将被操作系统挂起,直到该线程释放锁,其他线程才能对其进行访问,从而却确保了线程安全。
- (void)nslockTest {
NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"进入线程1");
sleep(2);
[lock lock];
NSLog(@"执行任务1");
[lock unlock];
NSLog(@"退出线程1");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"进入线程2");
[lock lock];
sleep(5);
NSLog(@"执行任务2");
[lock unlock];
NSLog(@"退出线程2");
});
}
运行程序,控制台输出:
通过结果,我们可以看到虽然程序先进入线程1,但是由于我们在执行lock
加入了延迟,由于是并发操作,所以紧接着,会进入线程2,线程2可以立即执行lock
操作,虽然我们紧接着sleep
了5秒钟,但是由于锁已经被线程2占用,并不会去执行线程1的操作,此时线程1就被阻塞了,只有等到线程2执行完成解锁之后才会进入线程1执行任务。这也就完美的体现了互斥锁的特性。
需要注意的是,NSLock
在使用不当的时候会造成堵塞线程。
网友评论