iOS-锁

作者: linbj | 来源:发表于2017-11-07 16:23 被阅读30次

iOS开发中知道的哪些锁? 哪个性能最差?

锁是线程编程同步工具的基础。iOS开发中常用的锁有如下几种:

  1. @synchronized

  2. NSLock 对象锁

  3. NSRecursiveLock 递归锁

  4. NSConditionLock 条件锁

  5. pthread_mutex 互斥锁(C语言)

  6. dispatch_semaphore 信号量实现加锁(GCD)

  7. OSSpinLock 参考YY

image
  • @synchronized 关键字加锁 互斥锁,性能较差不推荐使用
 @synchronized(这里添加一个OC对象,一般使用self) {
       这里写要加锁的代码
  }
 注意点
   1.加锁的代码尽量少
   2.添加的OC对象必须在多个线程中都是同一对象
    3.优点是不需要显式的创建锁对象,便可以实现锁的机制。
    4. @synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。

    //设置票的数量为5
    _tickets = 5;
    
    //线程1
    dispatch_async(dispatch_get_main_queue(), ^{
        [self saleTickets];
    });
    
    //线程2
    dispatch_async(dispatch_get_main_queue(), ^{
        [self saleTickets];
    });
    

- (void)saleTickets
{
    while (1) {
        @synchronized(self) {
            [NSThread sleepForTimeInterval:1];
            if (_tickets > 0) {
                _tickets--;
                NSLog(@"剩余票数= %ld, Thread:%@",_tickets,[NSThread currentThread]);
            } else {
                NSLog(@"票卖完了  Thread:%@",[NSThread currentThread]);
                break;
            }
        }
    }
}
8A998213-C7AA-4933-8A60-4DA6199AB641.png
  • NSLock
//加锁
- (void)lock;
//解锁
- (void)unlock;

在Cocoa程序中NSLock中实现了一个简单的互斥锁。
所有锁(包括NSLock)的接口实际上都是通过NSLocking协议定义的,它定义了lock和unlock方法。你使用这些方法来获取和释放该锁。

NSLock类还增加了tryLock和lockBeforeDate:方法。
tryLock试图获取一个锁,但是如果锁不可用的时候,它不会阻塞线程,相反,它只是返回NO。
lockBeforeDate:方法试图获取一个锁,但是如果锁没有在规定的时间内被获得,它会让线程从阻塞状态变为非阻塞状态(或者返回NO)。

还是卖票的例子

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //设置票的数量为5
    _tickets = 5;
    _mutexLock = [[NSLock alloc]init];
    
    //线程1
    dispatch_async(dispatch_get_main_queue(), ^{
        [self saleTickets];
    });
    
    //线程2
    dispatch_async(dispatch_get_main_queue(), ^{
        [self saleTickets];
    });
  
 }
- (void)saleTickets
{
    while (1) {
        [NSThread sleepForTimeInterval:1];
        //加锁
        [_mutexLock lock];
        if (_tickets > 0) {
            _tickets--;
            NSLog(@"剩余票数= %ld, Thread:%@",_tickets,[NSThread currentThread]);
        } else {
            NSLog(@"票卖完了  Thread:%@",[NSThread currentThread]);
            break;
        }
        //解锁
        [_mutexLock unlock];
    }
}

进入else 之后直接break退出循环,处于死锁状态


ECD3A209-767D-46F3-92C6-2EB4AB8A6E61.png

将方法改成如下能够解决死锁问题

- (void)saleTickets
{
    while (1) {
        [NSThread sleepForTimeInterval:1];
        //加锁
        [_mutexLock lock];
        if (_tickets > 0) {
            _tickets--;
            NSLog(@"剩余票数= %ld, Thread:%@",_tickets,[NSThread currentThread]);
        } else {
            NSLog(@"票卖完了  Thread:%@",[NSThread currentThread]);
            [_mutexLock unlock];
            break;
        }
        //解锁
        [_mutexLock unlock];
    }
}
    //创建锁
    _mutexLock = [[NSLock alloc]init];
    
    //线程1
    dispatch_async(dispatch_get_main_queue(), ^{
        static void(^TestMethod)(int);
        TestMethod = ^(int value)
        {
            [_mutexLock lock];
            if (value > 0)
            {
                [NSThread sleepForTimeInterval:1];
                TestMethod(value--);
            }
            [_mutexLock unlock];
        };
        TestMethod(5);
    });

在递归中使用NSLock 导致死锁


1B3DCB80-8337-4363-823B-5AA7E3068E61.png

在这种情况下,我们就可以使用NSRecursiveLock。它可以允许同一线程多次加锁,而不会造成死锁。递归锁会跟踪它被lock的次数。每次成功的lock都必须平衡调用unlock操作。只有所有达到这种平衡,锁最后才能被释放,以供其它线程使用。

//创建锁
    _rsLock = [[NSRecursiveLock alloc] init];

   //线程1
    dispatch_async(self.concurrentQueue, ^{
        static void(^TestMethod)(int);
        TestMethod = ^(int value)
        {
            [_rsLock lock];
            if (value > 0)
            {
                [NSThread sleepForTimeInterval:1];
                TestMethod(value--);
            }
            [_rsLock unlock];
        };

        TestMethod(5);
    });

使用如上代码即可解决递归加锁

  • NSConditionLock 条件锁

    //主线程中
    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尝试加锁失败");
        }
    });
4D6B9379-614A-4186-B940-3C7F389E9BF4.png

NSConditionLock也跟其它的锁一样,是需要lock与unlock对应的,只是lock,lockWhenCondition:与unlock,unlockWithCondition:是可以随意组合的

@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;

  • pthread_mutex 互斥锁
__block pthread_mutex_t mutex;
  pthread_mutex_init(&mutex, NULL);

  //线程1
  dispatch_async(self.concurrentQueue), ^{
      pthread_mutex_lock(&mutex);
      NSLog(@"任务1");
      sleep(2);
      pthread_mutex_unlock(&mutex);
  });

  //线程2
  dispatch_async(self.concurrentQueue), ^{
      sleep(1);
      pthread_mutex_lock(&mutex);
      NSLog(@"任务2");
      pthread_mutex_unlock(&mutex);
  });
  • dispatch_semaphore 信号量实现加锁
// 创建信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
         NSLog(@"任务1");
        sleep(10);
        dispatch_semaphore_signal(semaphore);
    });

    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务2");
        dispatch_semaphore_signal(semaphore);
    });

参考链接

相关文章

  • iOS-锁

    iOS开发中知道的哪些锁? 哪个性能最差? 锁是线程编程同步工具的基础。iOS开发中常用的锁有如下几种: @syn...

  • iOS-锁

    前言 有这么一个经典的案例,某一趟车次现在还有20张余票,火车站有4个售票窗口,人们开始排队买票。我们来模拟一下这...

  • iOS-锁的原理分析(二)

    前言 iOS-锁的原理分析(一)[https://www.jianshu.com/p/e11a980d819c]我...

  • iOS-锁-@synchronized

    @synchronized,同步锁,又名对象锁,由于其使用简单,基本上是在iOS开发中使用最频繁的锁。 使用方式如...

  • iOS-线程锁

    总有人会问线程安全问题,自己平常在项目中也没怎么遇到,没办法了,自能自己上网查找,最后总结了。 线程锁用来干什么的...

  • iOS-私有API与runtime

    iOS-私有API与runtime iOS-私有API与runtime

  • iOS-代码混淆加固策略

    iOS-代码混淆加固策略 iOS-代码混淆加固策略

  • iOS-性能优化深入探究

    iOS-性能优化深入探究 iOS-性能优化深入探究

  • iOS-自动打包及分发(三)

    iOS-自动打包及分发(一)iOS-自动打包及分发(二)iOS-自动打包及分发(三) 废话不多说了,上正文: 一、...

  • iOS-自动打包及分发(二)

    iOS-自动打包及分发(一)iOS-自动打包及分发(二)iOS-自动打包及分发(三) 本篇介绍:自动打包及分发xc...

网友评论

      本文标题:iOS-锁

      本文链接:https://www.haomeiwen.com/subject/bukjmxtx.html