上一篇大概介绍了一下锁的分类
这篇我们就解读一下第一个锁 NSLock
NSLock是Foundation提供的类,NSLock的API很少也很简单。常用的就几个方法
备注:深入解读 NSLock则需要 Foundation 源码, 但是这里公开的Foundation框架中只有NSObject的实现。假如我们想要查看NSString,NSArray,NSRunLoop,NSThread等Foundation这些类,是没有源码的。虽然通过汇编语言,一步步的跟踪也可以查看。但是汇编太过于晦涩难懂,所以这里推荐一个GNUstep。
GNUstep是GUN计划的项目之一,它将Cocoa的OC库重新开源实现了一遍,并且开源出来了。虽然GNUstep不是苹果官方的源码,是GNU计划写的,但是还是具有一定参考价值的。
GNUstep源码下载地址:http://www.gnustep.org/resources/downloads.php
一. NSLock 互斥锁 不能多次调用 lock方法,会造成死锁 ,遵循 NSLocking 协议。进行加锁、解锁
@protocol NSLocking
(void)lock;//加锁
(void)unlock;//解锁
@end
NSLock实现了NSLocking协议:
@interface NSLock : NSObject {
@private
void *_priv;
}
// 尝试获取锁,获取到返回YES,获取不到返回NO
(BOOL)tryLock;
// 在指定时间点之前获取锁,能够获取返回YES,获取不到返回NO
(BOOL)lockBeforeDate:(NSDate *)limit;
// 锁名称,如果使用锁出现异常,输出的log中会有
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
-tryLock:如果能获取到锁返回YES,如果获取不到锁返回NO,但不会使线程进入休眠,会继续向下执行
-lockBeforeDate::如果锁已被锁定,在指定的时间点之前线程进入休眠等待锁释放。如果在时间点之前锁被释放了,线程立即被唤醒获得锁,该函数会返回YES,继续执行任务,不会一直休眠等到那个时间点。如果等到时间点还没有获得锁会返回NO,并继续执行任务
NSLock是非递归锁,不能重入,否则会发生死锁:
上代码~~~
可以看到上面的代码最终只打印了testLock1,其他的几个打印不会去执行。因为 testLock1被锁了之后,还没有调用解锁就执行了testLock2。这个时候去lock 但是锁获取不到就休眠等待,直到testLock1 unlock解锁之后才会继续执行,但是这个时候testLock2 不执行完, testLock1 里面的代码也就被卡着不能继续。
注意:
-lock和-unlock必须在相同的线程调用,也就是说,他们必须在同一个线程中成对调用,否则会产生未知结果。
官方文档原文:Unlocking a lock from a different thread can result in undefined behavior.
可以阅读 GNUstep 的 Foundation (参考),也可以自行下载 swift 版本的 Foundation 源码分析
通过源码可知验证 NSLock 是对 pthread 中互斥锁 的封装。
其他都好理解,这里列一下 timedLock() 的实现流程:
1、设定超时时间,进入while循环。
2、pthread_cond_timedwait()在本次循环中计时等待,线程进入休眠
3、等待超时,直接返回 false;
4、如果等待没有超时,期间锁被释放,线程会被唤醒,再次尝试获取锁 pthread_mutex_trylock(),如果获取成功返回true
5、即没有超时,被唤醒后也没有成功获取到锁(被其他线程抢先获得锁),重新计算超时时间进入下一次while循环
NSLock 坑(网上看的例子,解读一下)
代码先执行第 38 行,然后执行第 31 行,此时锁被 lock,然后继续执行 if 里面,因为里面有 testMethod(value-1),所以又开始调用[lock lock]方法,此时的锁一直处于 lock 状态,if 还没执行完,没有 unlock,所以NSlog 只会打印一次