条件锁就是有特定条件的锁,所谓条件只是一个抽象概念,由程序猿自定义。说白了就是「有条件的互斥锁」.对于NSConditionLock
,官方文档的描述是这样的:
使用NSConditionLock对象,可以确保线程仅在满足特定条件时才能获取锁。 一旦获得了锁并执行了代码的关键部分,线程就可以放弃该锁并将关联条件设置为新的条件。 条件本身是任意的:您可以根据应用程序的需要定义它们。
NSConditionLock
同样实现了 NSLocking
协议,并定义了一些特定的接口:
@interface NSConditionLock : NSObject {
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
可以看到,所谓的条件condition
就是个NSInteger
,没有其他任何限制,使用起来就像UIView
的tag
一样。类的定义中,不带condition
的和 NSLock
没什么区别。特有的:
condition
,保存锁当前的条件-lockWhenCondition:
:获取锁,如果condition
与属性相等,则可以获得锁,否则阻塞线程,等待被唤醒-unlockWithCondition:
释放锁,并修改condition
属性
-(void)ljl_testCondition2
{
// 类似于信号量
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];
NSLog(@"线程2");
[conditionLock unlockWithCondition:1];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lock];
NSLog(@"线程3");
[conditionLock unlock];
});
}
//****************************************************
2020-04-06 21:45:38.273772+0800 filedome[1802:45978] 线程2
2020-04-06 21:45:38.274064+0800 filedome[1802:45870] 线程1
2020-04-06 21:45:38.274198+0800 filedome[1802:45871] 线程3
线程3 和 线程2谁先执行不一定,但是线程1一定是在线程2之后执行的。
执行线程1、2、3(顺序不确定的)
但线程1 、2获取锁需要看 condition 是否
满足
condition
置为1,满足线程1的条件,此时线程1获取到锁执行任务condition
置为0,不再执行任务在 [conditionLock lockWhenCondition:1]; 打断点。汇编查看
然后下符号断点 lockWhenCondition: + -> Symbolic BreakPoint... -> -[NSConditionLock lockWhenCondition:]
然后下符号断点 unlockWhenCondition: + -> Symbolic BreakPoint... -> -[NSConditionLock unlockWhenCondition:]
这个date 哪来的干什么的不知道 看一下文档。 xcode中选中 lockWhenCondition -> Help -> Search Documentation for Selected Text
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
被锁的话此方法将阻塞线程的执行,直到解锁或超时为止。
通过调试,发现NSConditionLock 是对 NSCondition 的封装。通过加锁来解决线程完全问题,那么条件是怎么什么地方处理的呢?
最后发现有调用了 waitUntilDate: 这个跟 [_testCondition wait]; 是一样的,条件不一样的时候进行等待。
即使在未来某个时间点可以满足条件,但-tryLockWhenCondition:
只是根据当前condition
获取锁
无论能否获取到锁,该线程都会继续向下执行,不会阻塞
//主线程中
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lockWhenCondition:1];
NSLog(@"线程1");
sleep(2);
[lock unlock];
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保证让线程2的代码后执行
if ([lock tryLockWhenCondition:0]) {
NSLog(@"线程2");
[lock unlockWithCondition:2];
NSLog(@"线程2解锁成功");
} else {
NSLog(@"线程2尝试加锁失败");
}
});
//线程3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);//以保证让线程2的代码后执行
if ([lock tryLockWhenCondition:2]) {
NSLog(@"线程3");
[lock unlock];
NSLog(@"线程3解锁成功");
} else {
NSLog(@"线程3尝试加锁失败");
}
});
//线程4
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(3);//以保证让线程2的代码后执行
if ([lock tryLockWhenCondition:2]) {
NSLog(@"线程4");
[lock unlockWithCondition:1];
NSLog(@"线程4解锁成功");
} else {
NSLog(@"线程4尝试加锁失败");
}
});
2016-08-19 13:51:15.353 ThreadLockControlDemo[1614:110697] 线程2
2016-08-19 13:51:15.354 ThreadLockControlDemo[1614:110697] 线程2解锁成功
2016-08-19 13:51:16.353 ThreadLockControlDemo[1614:110689] 线程3
2016-08-19 13:51:16.353 ThreadLockControlDemo[1614:110689] 线程3解锁成功
2016-08-19 13:51:17.354 ThreadLockControlDemo[1614:110884] 线程4
2016-08-19 13:51:17.355 ThreadLockControlDemo[1614:110884] 线程4解锁成功
2016-08-19 13:51:17.355 ThreadLockControlDemo[1614:110884] 线程1
上面代码先输出了 ”线程 2“,因为线程 1 的加锁条件不满足,初始化时候的 condition 参数为 0,而加锁条件是 condition 为 1,所以加锁失败。locakWhenCondition 与 lock 方法类似,加锁失败会阻塞线程,所以线程 1 会被阻塞着,而 tryLockWhenCondition 方法就算条件不满足,也会返回 NO,不会阻塞当前线程。
回到上面的代码,线程 2 执行了 [lock unlockWithCondition:2]; 所以 Condition 被修改成了 2。
而线程 3 的加锁条件是 Condition 为 2, 所以线程 3 才能加锁成功,线程 3 执行了 [lock unlock]; 解锁成功且不改变 Condition 值。
线程 4 的条件也是 2,所以也加锁成功,解锁时将 Condition 改成 1。这个时候线程 1 终于可以加锁成功,解除了阻塞。
从上面可以得出,NSConditionLock 还可以实现任务之间的依赖。
NSConditionLock
也实现了NSLocking
协议,可以通过-lock
加锁以及-unlock
解锁,但无论当前condition
是多少,-lock
都可以获得锁,-unlock
也不会改变当前condition
的值。condition
满足线程1的持锁条件,但线程2通过-lock
获取没有条件的锁,此时他们谁先抢到锁就执行谁的任务,另一个就要等锁释放。-lockWhenCondition:
获取锁时,线程会休眠等待,必须有其他线程设置为合适的条件,否则该线程永远获取不到锁熟悉了条件锁NSConditionLock
的使用方法,可以用一个任务编号(条件)来指定不同线程任务的执行顺序。
先看NSConditionLock
的属性:
open class NSConditionLock : NSObject, NSLocking {
internal var _cond = NSCondition()
internal var _value: Int
internal var _thread: _swift_CFThreadRef?
}
_cond
:一个NSCondition
对象_value
:记录当前条件的值_thread
:记录当前线程无论是-lock
还是-lockWhenCondition:
都会走这个函数:
open func lock(whenCondition condition: Int, before limit: Date) -> Bool {
_cond.lock()
while _thread != nil || _value != condition {
if !_cond.wait(until: limit) {
_cond.unlock()
return false
}
}
_thread = pthread_self()
_cond.unlock()
return true
}
condition
不相等,则再一次休眠等待,否则可以获得锁释放条件锁:
open func unlock(withCondition condition: Int) {
_cond.lock()
_thread = nil
_value = condition
_cond.broadcast()
_cond.unlock()
}
broadcast()
唤醒所有等待在这个锁的线程