iOS技术图谱

iOS技术图谱之多线程安全问题

2019-11-15  本文已影响0人  iOS大蝠

多线程安全问题

多个线程访问同一块资源进行读写,如果不加控制随意访问容易产生数据错乱从而引发数据安全问题。为了解决这一问题,就有了加锁的概念。加锁的原理就是当有一个线程正在访问资源进行写的时候,不允许其他线程再访问该资源,只有当该线程访问结束后,其他线程才能按顺序进行访问。对于读取数据,有些程序设计是允许多线程同时读的,有些不允许。UIKit中几乎所有控件都不是线程安全的,因此需要在主线程上更新UI。

为了解决多线程访问安全问题,iOS提供了以下几种方式来通过加锁保证多个线程访问共享资源(临界区)安全。
(1)@synchronized
(2)NSLock
(3)NSConditionLock
(4)NSRecursiveLock

以上4种加锁方式我们通过例子来一一介绍。

@synchronized

    self.tickets = 200;
    
    NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    
    thread1.name = @"售票员1";
    
    
    NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    
    thread2.name = @"售票员2";
    
    [thread1 start];
    [thread2 start];

- (void)saleTickets{
    while (YES) {
        [NSThread sleepForTimeInterval:2];
      //互斥锁 -- 保证锁内的代码在同一时间内只有一个线程在执行
        @synchronized (self) {
            NSLog(@"%@ -- 开始卖票",[NSThread currentThread].name);
            if (self.tickets>0) {
                self.tickets--;
                NSLog(@"剩余票数 = %ld",self.tickets);
            }else{
                NSLog(@"票卖完了");
                break;
            }
        }
    }
}

通过互斥锁,我们可以看到,它保证了同一时间只有一个线程来访问临界区,这样self.ticket的值可以保证安全。

NSLock

线程代码一样

执行代码我们使用NSLock

- (void)saleTickets{
    while (YES) {
        [NSThread sleepForTimeInterval:2];
//        @synchronized (self) {
        [self.lock lock];
            NSLog(@"%@ -- 开始卖票",[NSThread currentThread].name);
            if (self.tickets>0) {
                self.tickets--;
                NSLog(@"剩余票数 = %ld",self.tickets);
            }else{
                NSLog(@"票卖完了");
                break;
            }
//        }
        [self.lock unlock];
    }
}

NSConditionLock(条件锁)

使用此锁,在线程没有获得锁的情况下,阻塞,即暂停运行,典型用于生产者/消费者模型。

- (instancetype)initWithCondition:(NSInteger)condition;//初始化条件锁
- (void)lockWhenCondition:(NSInteger)condition;//加锁 (条件是:锁空闲,即没被占用;条件成立)
- (BOOL)tryLock; //尝试加锁,成功返回TRUE,失败返回FALSE
- (BOOL)tryLockWhenCondition:(NSInteger)condition;//在指定条件成立的情况下尝试加锁,成功返回TRUE,失败返回FALSE
- (void)unlockWithCondition:(NSInteger)condition;//在指定的条件成立时,解锁
- (BOOL)lockBeforeDate:(NSDate *)limit;//在指定时间前加锁,成功返回TRUE,失败返回FALSE,
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;//条件成立的情况下,在指定时间前加锁,成功返回TRUE,失败返回FALSE,
@property (readonly) NSInteger condition;//条件锁的条件
@property (nullable, copy) NSString *name;//条件锁的名称

聚个例子:

NSConditionLock* myCondition=[[NSConditionLock alloc]init];
    [NSThread detachNewThreadWithBlock:^{
        for(int i=0;i<5;i++)
        {
            [myCondition lock];
            NSLog(@"当前解锁条件:%d",i);
            sleep(2);
            [myCondition unlockWithCondition:i];
            BOOL isLocked=[myCondition tryLockWhenCondition:2];
            if(isLocked)
            {
                NSLog(@"加锁成功!!!!!");
                [myCondition unlock];
            }
        }
    }];

NSRecursiveLock(递归锁)

此锁可以在同一线程中多次被使用,但要保证加锁与解锁使用平衡,多用于递归函数,防止死锁。

- (BOOL)tryLock;//尝试加锁,成功返回TRUE,失败返回FALSE
- (BOOL)lockBeforeDate:(NSDate *)limit;//在指定时间前尝试加锁,成功返回TRUE,失败返回FALSE
@property (nullable, copy) NSString *name;//线程锁名称

举个例子:

-(void)initRecycle:(int)value
{
   [myRecursive lock];
   if(value>0)
   {
       NSLog(@"当前的value值:%d",value);
       sleep(2);
       [self initRecycle:value-1];
   }
   [myRecursive unlock];
}
上一篇下一篇

猜你喜欢

热点阅读