iOS 加锁的方式

iOS多线程编程中,经常碰到多个线程访问共同的一个资源,在线程相互交互的情况下,需要一些同步措施,来保证线程之间交互的时候是安全的。下面我们一起看一下学一下iOS的几种常用的加锁方式,希望对大家有所帮助!!!

  1. @synchronized
  2. NSLock对象锁
  3. NSRecursiveLock递归锁
  4. NSConditionLock条件锁
  5. dispatch_semaphore 信号量实现加锁(也就是GCD)

介绍与使用

1.@synchronized

@synchronized关键字加锁,互斥锁,性能较差不推荐在项目中使用。

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

下面我们以一个最经典的例子:卖票

//设置票的数量为5
    _tickets = 5;
    
    //线程1
    dispatch_async(self.concurrentQueue, ^{
        [self saleTickets];
    });
    
    //线程2
    dispatch_async(self.concurrentQueue, ^{
        [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;
            }
        }
    }
}

2.NSLock

基本所有锁的接口都是通过NSLocking协议定义的,定义了lock和unlock方法,通过这些方法获取和释放锁。

下面还是以卖票的例子讲述一下。

//设置票的数量为5
    _tickets = 5;
    
    //创建锁
    _mutexLock = [[NSLock alloc] init];
    
    //线程1
    dispatch_async(self.concurrentQueue, ^{
        [self saleTickets];
    });
    
    //线程2
    dispatch_async(self.concurrentQueue, ^{
        [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];
    }
}

3.NSRecursiveLock递归锁

使用锁比较容易犯的错误是在递归或者循环中造成死锁。

如下代码锁会被多次lock,造成自己被阻塞。

//创建锁
    _mutexLock = [[NSLock alloc]init];
  
    //线程1
    dispatch_async(self.concurrentQueue, ^{
        static void(^TestMethod)(int);
        TestMethod = ^(int value)
        {
            [_mutexLock lock];
            if (value > 0)
            {
                [NSThread sleepForTimeInterval:1];
                TestMethod(value--);
            }
            [_mutexLock unlock];
        };
        
        TestMethod(5);
    });

如果把这个NSLock换成NSRecursiveLock,就可以解决问题。

NSRecursiveLock类定义的锁,可以在同一线程多次lock,不会造成死锁。

//创建锁
    _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);
    });

4.NSConditionLock条件锁

NSMutableArray *products = [NSMutableArray array];  
NSInteger HAS_DATA = 1;  
NSInteger NO_DATA = 0;  
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
    while (1) {  
        [lock lockWhenCondition:NO_DATA];  
        [products addObject:[[NSObject alloc] init]];  
        NSLog(@"produce a product,总量:%zi",products.count);  
        [lock unlockWithCondition:HAS_DATA];  
        sleep(1);  
    }  
});  
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
    while (1) {  
       NSLog(@"wait for product");  
        [lock lockWhenCondition:HAS_DATA];  
       [products removeObjectAtIndex:0];  
       NSLog(@"custome a product");  
       [lock unlockWithCondition:NO_DATA];  
    }  
});

在线程1中的加锁使用了lock,所以是不要条件的,也就锁住了。但在unlock的使用整型条件,它可以开启其他线程中正在等待钥匙的临界池,当线程1循环到一次的时候,打开了线程2的阻塞。

NSCoditionLock中lock,lockWhenCondition:与unlock,unlockWithCondition:是可以随意组合的,具体使用根据需求来区分。

5.dispatch_semaphore信号量实现加锁

dispatch_semaphore_t signal = dispatch_semaphore_create(1);  
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);  
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
    dispatch_semaphore_wait(signal, overTime);  
            NSLog(@"需要线程同步的操作1 开始");  
            sleep(2);  
            NSLog(@"需要线程同步的操作1 结束");  
        dispatch_semaphore_signal(signal);  
});  
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
        sleep(1);  
        dispatch_semaphore_wait(signal, overTime);  
            NSLog(@"需要线程同步的操作2");  
        dispatch_semaphore_signal(signal);  
});  

dispatch_semaphore是GCD用于同步的方式,与之相关的共有三个函数,dispatch_semaphore_wait,dispatch_semaphore_signal,dispatch_semaphore_create。

(1)dispatch_semaphore_create的声明为:

dispatch_semaphore_t dispatch_semaphore_create(long value);

传入的参数是long类型,输出一个dispatch_semaphore_t类型值为Value的信号量(value传入值不能小于0,否则会报错NULL)

(2)dispatch_semaphore_signal声明为下面:

long dispatch_semaphore_signal(dispatch_semaphore_t dsema); 

这个方法会使dsema加1;

(3)dispatch_semaphore_wait的声明为下面:

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

这个方法会使dsema减1。

整个逻辑如下:

如果dsema信号量值为大于0,该函数所在线程就会继续执行下面的语句,并将信号量的减去1;如果dsema为0时,函数就会阻塞当前的线程,如果等待的期间发现dsema的值被dispatch_semaphore_signal加1了,并且该函数得到了信号量,那么继续向下执行,并将信号量减1,如果等待期间没有获得信号量或者值一直为0,那么等到timeout,所处的线程也会自动执行下面的代码。

dispatch_semaphore,当信号量为1时,可以作为锁使用。如果没有出现等待的情况,它的性能比pthread_mutex还要高,当如果有等待情况的时候,性能就会下降很多,相比OSSpinLock(暂不讲解),它的优势在于等待的时侯不会消耗CPU资源。

针对上面代码,发现如果超时时间overTime>2,可完成同步操作,反之,在线程1还没有执行完的情况下,此时超时了,将自动执行下面的代码。

上面代码执行结果:

2018-09-18 15:40:52.324 SafeMultiThread[35945:579032] 需要线程同步的操作1 开始  
2018-09-18 15:40:52.325 SafeMultiThread[35945:579032] 需要线程同步的操作1 结束  
2018-09-18 15:40:52.326 SafeMultiThread[35945:579033] 需要线程同步的操作2  

如果将overTime<2s的时候,执行为

2018-09-18 15:40:52.049 SafeMultiThread[30834:434334] 需要线程同步的操作1 开始  
2018-09-18 15:40:52.554 SafeMultiThread[30834:434332] 需要线程同步的操作2  
2018-09-18 15:40:52.054 SafeMultiThread[30834:434334] 需要线程同步的操作1 结束  

以上就是自己在开发中所经常使用到的加锁方式,希望对大家有所帮助!!!

猜你喜欢

转载自www.cnblogs.com/guohai-stronger/p/9663459.html