NSRecursiveLock
,递归锁,顾名思义,执行递归操作的时候使用的锁。是互斥锁中的递归锁,可被同一线程多次获取的锁,而不会产生死锁。什么意思呢,一个线程已经获得了锁,开始执行受锁保护的代码(锁还未释放),如果这段代码调用了其他函数,而被调用的函数又要获取这个锁,此时已然可以获得锁并正常执行,而不会死锁,前提是在同一线程。
下面我们看一个例子。
- (void)recursiveTest {
NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^recursiveMethod)(int);
recursiveMethod = ^(int value){
[lock lock];
if (value > 0) {
NSLog(@"value==%d",value);
recursiveMethod(value - 1);
}
[lock unlock];
};
recursiveMethod(10);
});
}
控制台输出:
value==10
这是为什么呢?recursiveMethod
在第一次调用的时候,就会在方法内部使用NSLock
加了一把锁,第二次调用的时候第一把锁还未打开,程序就被阻塞了,所以控制台只输出了一行。这是我们就需要另一种锁,递归锁,NSRecursiveLock
,将上述例子的NSLock
换成NSRecursiveLock
,程序运行正常。
打开Objective-C
代码,我们发现并不能看到NSRecursiveLock
的具体实现:
@interface NSRecursiveLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
可以查看swift
关于CoreFoundation
框架的开源代码:
public override init() {
super.init()
var attrib = pthread_mutexattr_t()
withUnsafeMutablePointer(to: &attrib) { attrs in
pthread_mutexattr_init(attrs)
// 设置为递归属性
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
pthread_mutex_init(mutex, attrs)
}
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)
}
open func `try`() -> Bool {
return pthread_mutex_trylock(mutex) == 0
}
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)
}
#define PTHREAD_MUTEX_NORMAL 0 //普通非递归锁
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2 //递归锁
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
我们也可以看出NSRecursiveLock
是对pthread_mutex
的封装,也就是说NSRecursiveLock
和NSLock
是同源的,都是互斥锁。更细分一些,NSRecursiveLock
是递归锁,递归锁也是一种互斥锁。
我们再给上述例子外层加一个循环,让其循环100次,即多线程访问的递归操作。此时运行程序,我们会发现,程序会崩溃在[lock unlock]
这个方法,这是因为发生了死锁。多线程加锁解锁的时候,会出现互相等待解锁的情况,比如当线程2调用lock
方法,在还没unlock
的时候,线程3也调用了lock
方法,这时候线程2已经lock
,所以线程3需要等待线程2来unlock
,线程2要等到线程3来unlock
。这个时候,我们把程序做如下改动,去掉NSRecursiveLock
的部分,使用@synchronized
代替:
@synchronized (self) {
if (value > 0) {
NSLog(@"value==%d",value);
recursiveMethod(value - 1);
}
}
发现程序运行正常,这是因为@synchronized
在底层做了特殊处理,维护了一个threadCount
和一个lockCount
,对相应的加锁解锁进行加减操作,而且当对象被释放的时候还不会做任何事。
总结
递归锁其实也是一种互斥锁,由于其递归的属性,可被同一线程多次获取的锁,而不会产生死锁。但是如果其被多线程操作的时候,也可能因为线程之间获取锁释放锁的互相等待而出现死锁情况。
网友评论