「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」
NSLock(互斥锁)
NSLock 是 Cocoa提供的最基本的锁对象,它是这样被定义的:
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : 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
关于 lock 和 unlock
- (void)viewDidLoad {
[super viewDidLoad];
NSLock *lock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"线程1");
sleep(2);
[lock unlock];
NSLog(@"线程1解锁成功");
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保证让线程2的代码后执行
[lock lock];
NSLog(@"线程2");
[lock unlock];
});
}
log:
线程1 中的 lock 锁上了,所以线程2 中的 lock 加锁失败,阻塞线程2(线程2的log没有打印),但2s后线程1 中的 lock 解锁,线程2 就立即加锁成功,执行线程2中的后续代码
互斥锁会使得线程阻塞,阻塞的过程又分两个阶段,第一阶段是会先空转,可以理解成跑一个 while 循环,不断地去申请加锁,在空转一定时间之后,线程会进入 waiting 状态,此时线程就不占用CPU资源了,等锁可用的时候,这个线程会立即被唤醒
- (BOOL)tryLock
使用 tryLock 会试图获取一个锁,如果锁不可用的时候,它不会阻塞线程,会直接返回 NO
- (void)viewDidLoad {
[super viewDidLoad];
NSLock *lock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"线程1");
sleep(2);
[lock unlock];
NSLog(@"线程1解锁成功");
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保证让线程2的代码后执行
//尝试加锁
if ([lock tryLock]) {
[lock lock];
NSLog(@"线程2");
[lock unlock];
}else{
NSLog(@"线程2尝试加锁失败");
}
});
}
log:
当线程1 中的 lock 锁上了,所以线程2 中的 tryLock 会尝试加锁,加锁失败会,返回 NO,然后都会执行后续代码
- (BOOL)lockBeforeDate:(NSDate *)limit
使用 lockBeforeDate: 方法会在所指定 Date 之前尝试加锁,会阻塞线程,如果在指定时间之前都不能加锁,则返回 NO,指定时间之前能加锁,则返回 YES
- (void)viewDidLoad {
[super viewDidLoad];
NSLock *lock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"线程1");
sleep(5);
[lock unlock];
NSLog(@"线程1解锁成功");
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保证让线程2的代码后执行
//尝试在未来的2s内获取锁,并阻塞该线程,如果2s内获取不到恢复线程, 返回NO,不会阻塞该线程
if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:2]]) {
[lock lock];
NSLog(@"线程2");
[lock unlock];
}else{
NSLog(@"线程2尝试加锁失败");
}
});
}
log:
tryLock 与 lock 的区别
tryLock 和 lock 方法都会请求加锁,唯一不同的是 tryLock 在没有获得锁的时候,不会卡线程,可以继续做一些任务和处理