OC基础原理

iOS锁系列-NSLock对象锁

2018-04-16  本文已影响254人  lltree

原文,本文只为学习

对象锁:代码为[临界区:共同访问一段代码]synchronized(Object)语句指定的对象进行加锁

示例:

//主线程中
    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];
    });

2016-08-19 14:23:09.659 ThreadLockControlDemo[1754:129663] 线程1
2016-08-19 14:23:11.663 ThreadLockControlDemo[1754:129663] 线程1解锁成功
2016-08-19 14:23:11.665 ThreadLockControlDemo[1754:129659] 线程2

NSLock的执行原理:
线程 1 中的 lock 锁上了,所以线程 2 中的 lock 加锁失败,阻塞线程 2,但 2 s 后线程 1 中的 lock 解锁,线程 2 就立即加锁成功,执行线程 2 中的后续代码。

查到的资料显示互斥锁会使得线程阻塞,阻塞的过程又分两个阶段,第一阶段是会先空转,可以理解成跑一个 while 循环,不断地去申请加锁,在空转一定时间之后,线程会进入 waiting 状态,此时线程就不占用CPU资源了,等锁可用的时候,这个线程会立即被唤醒。

所以如果将上面线程 1 中的 sleep(2); 改成 sleep(10); 输出的结果会变成

2016-08-19 14:25:16.226 ThreadLockControlDemo[1773:131824] 线程1
2016-08-19 14:25:26.231 ThreadLockControlDemo[1773:131831] 线程2
2016-08-19 14:25:26.231 ThreadLockControlDemo[1773:131824] 线程1解锁成功

从上面的两个输出结果可以看出,线程 2 lock 的第一秒,是一直在轮询请求加锁的,因为轮询有时间间隔,所以 ”线程 2“ 的输出晚于 ”线程 1 解锁成功“,但线程 2 lock 的第九秒,是当锁可用的时候,立即被唤醒,所以 ”线程 2“ 的输出早于 ”线程 1 解锁成功“。多做了几次试验,发现轮询 1 秒之后,线程会进入 waiting 状态。

//主线程中
    NSLock *lock = [[NSLock alloc] init];
    
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"线程1");
        sleep(10);
        [lock unlock];
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);//以保证让线程2的代码后执行
        if ([lock tryLock]) { //尝试加锁
            NSLog(@"线程2");
            [lock unlock];
        } else {
            NSLog(@"尝试加锁失败");
        }
    });

2016-08-19 11:42:54.433 ThreadLockControlDemo[1256:56857] 线程1
2016-08-19 11:42:55.434 ThreadLockControlDemo[1256:56861] 尝试加锁失败

由上面的结果可得知,tryLock 并不会阻塞线程。[lock tryLock] 能加锁返回 YES,不能加锁返回 NO,然后都会执行后续代码。

如果将 [lock tryLock] 替换成

[lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]

的话,则会返回 YES,输出 “线程 2“,lockBeforeDate: 方法会在所指定 Date 之前尝试加锁,会阻塞线程,如果在指定时间之前都不能加锁,则返回 NO,指定时间之前能加锁,则返回 YES。

@protocol NSLocking

- (void)lock;//加锁
- (void)unlock;//解锁

@end

@interface NSLock : NSObject <NSLocking> {
@private
    void *_priv;
}

//尝试加锁,不会阻塞线程。true则加锁成功,false则失败,说明其他线程在加锁中这个方法无论如何都会立即返回。
- (BOOL)tryLock;

//尝试在指定NSDate之前加锁,会阻塞线程。true则加锁成功,false则失败,说明其他线程在加锁中这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
- (BOOL)lockBeforeDate:(NSDate *)limit;

//name 是用来标识用的
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

@end

上一篇 下一篇

猜你喜欢

热点阅读