美文网首页iOS-机制IOS开发
iOS -锁-NSRecursiveLock

iOS -锁-NSRecursiveLock

作者: xxxxxxxx_123 | 来源:发表于2020-03-13 16:52 被阅读0次

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的封装,也就是说NSRecursiveLockNSLock是同源的,都是互斥锁。更细分一些,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,对相应的加锁解锁进行加减操作,而且当对象被释放的时候还不会做任何事。

总结

递归锁其实也是一种互斥锁,由于其递归的属性,可被同一线程多次获取的锁,而不会产生死锁。但是如果其被多线程操作的时候,也可能因为线程之间获取锁释放锁的互相等待而出现死锁情况。

相关文章

网友评论

    本文标题:iOS -锁-NSRecursiveLock

    本文链接:https://www.haomeiwen.com/subject/ihpdshtx.html