美文网首页iOS技术图谱
iOS技术图谱之多线程安全问题

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

作者: iOS大蝠 | 来源:发表于2019-11-15 21:43 被阅读0次

    多线程安全问题

    多个线程访问同一块资源进行读写,如果不加控制随意访问容易产生数据错乱从而引发数据安全问题。为了解决这一问题,就有了加锁的概念。加锁的原理就是当有一个线程正在访问资源进行写的时候,不允许其他线程再访问该资源,只有当该线程访问结束后,其他线程才能按顺序进行访问。对于读取数据,有些程序设计是允许多线程同时读的,有些不允许。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];
    }
    

    相关文章

      网友评论

        本文标题:iOS技术图谱之多线程安全问题

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