iOS中的线程锁(关于NSConditionLock)

1,460 阅读2分钟

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

NSConditionLock(条件锁)

NSConditionLock 概念

NSConditionLock 是对 NSCondition 做了一层封装,通过条件变量来控制加锁、解锁,从而达到阻塞线程、唤醒线程的目的

NSConditionLock 常用方法 <遵守 NSLocking 协议>

  • 如果没有其他线程加锁,但是该锁内部的 condition 条件不等于传入值,此行以下代码不能加锁,进入等待状态,会阻塞线程。如果锁内部的 condition 条件等于传入值,并且没有其他线程加锁,则进⼊代码区。同时设置加锁,其他任何线程都将等待它的代码完成,直⾄它解锁

    - (void)lockWhenCondition:(NSInteger)condition;
    
  • 解锁操作,同时把内部的 condition 条件设置为传入值

    - (void)unlockWithCondition:(NSInteger)condition;
    
  • 尝试加锁,当锁的条件值与传入值相等,则加锁成功,否则失败返回 NO,不会阻塞线程

    - (BOOL)tryLockWhenCondition:(NSInteger)condition;
    
  • 指定时间前尝试加锁,当锁的条件值与传入值相等,则加锁成功返回 YES,否则失败返回 NO,到时间前阻塞线程

    - (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
    

NSConditionLock 常用场景

使用 NSConditionLock 能够解决回调顺序的场景

- (void)viewDidLoad{
    [super viewDidLoad];

    NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];

    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [lock lockWhenCondition:1];
        NSLog(@"线程1");
        [lock unlock];
    });

    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(4);//以保证让线程2的代码后执行
        
        if ([lock tryLockWhenCondition:0]) {
            NSLog(@"线程2");
            [lock unlockWithCondition:1];
            NSLog(@"线程2解锁成功");
        } else {
            NSLog(@"线程2尝试加锁失败");
        }
    });

    //线程3
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(2);//以保证让线程3的代码后执行
        
        if ([lock tryLockWhenCondition:2]) {
            NSLog(@"线程3");    
            [lock unlock];
            NSLog(@"线程3解锁成功");
        } else {
            NSLog(@"线程3尝试加锁失败");
        }
    });
}

log:

Snip20211117_22.png

1、初始化 NSConditionLock 对象,并设置 condition 为0

2、当线程1调用 [lock lockWhenCondition:1],此刻因为不满足当前条件,所以会进入等待状态,会阻塞线程

3、线程3调用 [lock tryLockWhenCondition:2],此刻因为不满足当前条件,但是不会阻塞线程,所以打印出:线程3尝试加锁失败

4、线程2调用 [lock tryLockWhenCondition:0],因为当锁的条件值与传入值相等,会输出线程2的打印,打印完成后会调用 [lock unlockWithCondition:1],这个时候将条件设置为1,并发送 boradcast, 此时线程1接收到当前的信号,唤醒线程1并完成线程1的打印