iOS 开发,各种锁你了解多少?NSLock、NSCondtion、NSRecursiveLock…
回顾
在上篇博客中已经对NSLock 、NSCondtion 、NSRecursiveLock 、NSCondition 等锁进行了举例分析,那么本篇博客就继续分析锁,从Foundation 源码分析锁! 
iOS底层探索之多线程(一)—进程和线程
iOS底层探索之多线程(二)—线程和锁
iOS底层探索之多线程(三)—初识GCD
iOS底层探索之多线程(四)—GCD的队列
iOS底层探索之多线程(五)—GCD不同队列源码分析
iOS底层探索之多线程(六)—GCD源码分析(sync 同步函数、async 异步函数)
iOS底层探索之多线程(七)—GCD源码分析(死锁的原因)
iOS底层探索之多线程(八)—GCD源码分析(函数的同步性、异步性、单例)
iOS底层探索之多线程(九)—GCD源码分析(栅栏函数)
iOS底层探索之多线程(十)—GCD源码分析( 信号量)
iOS底层探索之多线程(十一)—GCD源码分析(调度组)
iOS底层探索之多线程(十二)—GCD源码分析(事件源)
iOS底层探索之多线程(十三)—锁的种类你知多少?
iOS底层探索之多线程(十四)—关于@synchronized锁你了解多少?
iOS底层探索之多线程(十五)—@synchronized源码分析
iOS底层探索之多线程(十六)——锁分析(NSLock、NSCondtion、NSRecursiveLock、NSCondition)
1. 从Foundation源码分析锁
上篇博客对NSLock 、NSCondtion 、NSRecursiveLock 等锁的使用,进行了代码的举例和分析,那么他们其实都是对pthread 锁进行了一层封装。通过查看 NSLock 的 API ,发现是在Foundation 框架下的如图:  通过查看 API 发现,锁都是遵循了一个叫做NSLocking 的协议,所以大部分锁都是有如下两个方法的:
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
但是OC 的Foundation 框架是不开源的,那难道我们的探索就止步于此了吗?有没有其他的方式来探索呢?
有的,我们可以通过 swift 的Foundation 框架的开源源码来看看锁是如何封装实现的,说干就干!👉源码戳这里
 从 swift 的Foundation 的源码工程中搜索NSLocking 协议,发现这里针对不同的平台进行了一些设置和初始化工作,很明显可以看出来是对pthread 的封装。
还有构造方法 (init)、析构方法 (deinit)、加锁 (lock)、解锁 (unlock)的方法定义,如下: 
- 构造方法
init() 就是调用了pthread 的pthread_mutex_init(mutex, nil) 方法 - 析构方法
deinit 就是调用了pthread 的pthread_mutex_destroy(mutex) 方法 - 加锁方法
lock() 就是调用了pthread 的pthread_mutex_lock(mutex) 方法 - 解锁方法
unlock() 就是调用了pthread 的pthread_mutex_unlock(mutex) 方法
通过NSLock 和NSRecursiveLock 方法的对比,发现它们的lock() 方法都是一样的,都调用了pthread_mutex_lock(mutex) ,如下:

那为什么NSRecursiveLock 就可以支持可递归加锁呢?why ?为什么呢!请继续往下看:
 NSRecursiveLock 方法支持可递归加锁,原因就是图中所示,在init 方法中,通过pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE)) 进行了递归的设置,这一波操作真是很细节,非常的人性化的设计,着实到位👍(ps:哈哈)
 通过搜索,发现NSCondition 方法也是对pthread 进行了封装,就是多了个对 cond 的处理,除了进行pthread_mutex 互斥处理外,还对pthread_cond 进行了处理,同时提供了wait 、signal 、broadcase 等方法,如下图所示: 
 NSConditionLock 方法,里面没有直接对进行pthread_mutex 进行封装,只是属性里面使用了NSCondition 类型的_cond 属性和一个_swift_CFThreadRef 类型的属性,通过这两个属性,实现加锁和线程方面的相关处理。
NSCondition 里面是包含了对pthread_mutex 的封装_swift_CFThreadRef 其实是等于pthread_t 
2. NSLocationLock分析
先来看看下面👇的代码,猜猜打印的顺序:
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[conditionLock lockWhenCondition:1];
NSLog(@"线程 1");
[conditionLock unlockWithCondition:0];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[conditionLock lockWhenCondition:2];
sleep(0.1);
NSLog(@"线程 2");
[conditionLock unlockWithCondition:1];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lock];
NSLog(@"线程 3");
[conditionLock unlock];
});

这里线程 2 一定比线程 1 先执行,因为NSConditionLock 锁,?旦?个线程获得锁,其他线程?定会等待,在初始化的时候设置了条件2 ,所以线程 2 的优先级 高于线程 1 。
[xxxx lock] :表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition ) 那它能执?此?以下代码,如果已经有其他线程获得锁(可能是条件锁,或者?条件锁),则等待,直?其他线程解锁 [xxx lockWhenCondition:A条件] :表示如果没有其他线程获得该锁,但是该锁内部的condition 不等于A 条件,它依然不能获得锁,仍然等待。如果内部的condition 等于A 条件,并且没有其他线程获得该锁,则进?代码区,同时设置它获得该锁,其他任何线程都将等待它代码的完成,直?它解锁。[xxx unlockWithCondition:A条件] : 表示释放锁,同时把内部的condition 设置为A 条件return = [xxx lockWhenCondition:A条件 beforeDate:A时间] :表示如果被锁定(没获得锁),并超过该时间则不再阻塞线程 。但是注意 :返回的值是NO ,它没有改变锁的状态,这个函数的?的在于可以实现两种状态下的处理- 所谓的
condition 就是整数,内部通过整数?较条件
线程 1 调?[NSConditionLock lockWhenCondition:] ,此时此刻因为不满?当前条件,所以会进? waiting 状态,当前进?到waiting 时,会释放当前的互斥锁。- 此时当前的
线程 3 调?[NSConditionLock lock:] ,本质上是调?[NSConditionLock lockBeforeDate:] ,这?不需要?对条件值,所以线程 3 会打印。 - 接下来
线程 2 执?[NSConditionLock lockWhenCondition:] ,因为满?条件值,所以线程2 会打印,打印完成后会调?[NSConditionLock unlockWithCondition:] ,这个时候把value 设置为 1 ,并发送boradcast , 此时线程 1 接收到当前的信号,唤醒执? 并打印。 - ?此当前打印为
线程 3 --> 线程 2 --> 线程 1 。 [NSConditionLock lockWhenCondition:] :这?会根据传?的 condition 值和value 值进?对?,如果不相等,这?就会阻塞,进?线程池,否则的话就继续代码执?。[NSConditionLock unlockWithCondition:] : 这?会先更改当前的value 值,然后进??播,唤醒当前的线程。
3.总结
- 以上的锁,其实都是对
pthread 封装,你如果对pthread 比较熟悉,能直接用就直接用,不能就用这些封装的就可以 - 使用锁的时候,根据自己的业务场景来合理的使用互斥锁、递归锁、条件锁
更多内容持续更新
🌹 喜欢就点个赞吧👍🌹
🌹 觉得有收获的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹
🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹
|