iOS开发多线程--线程安全

作者: 爱吃鱼的小灰 | 来源:发表于2016-11-25 17:19 被阅读128次

    多线程的安全隐患

    资源共享
    一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。
    有问题的代码:

    @interface ViewController ()
     //剩余票数
    @property(nonatomic,assign) int leftTicketsCount;
    @property(nonatomic,strong)NSThread *thread1;
     @property(nonatomic,strong)NSThread *thread2;
     @property(nonatomic,strong)NSThread *thread3;
     @end
     @implementation ViewController
     - (void)viewDidLoad
     {
      [super viewDidLoad];
        //默认有20张票
        self.leftTicketsCount=10;
        //开启多个线程,模拟售票员售票
        self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
        self.thread1.name=@"售票员A";
        self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
        self.thread2.name=@"售票员B";
        self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
        self.thread3.name=@"售票员C";
    }
    - (void)sellTickets
     {
     while (1) {
     //1.先检查票数
     int count=self.leftTicketsCount;
     if (count>0) {
         //暂停一段时间
      [NSThread sleepForTimeInterval:0.002];
      //2.票数-1
      self.leftTicketsCount= count-1;
       //获取当前线程
      NSThread *current=[NSThread currentThread];
     NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);
     }else
       {
     //退出线程
              [NSThread exit];
          }
       }
     }
     -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
     { 
      //开启线程
     [self.thread1 start];
       [self.thread2 start];
        [self.thread3 start];
    }
     @end
    

    结果是数据错乱:


    1.png
    • 安全隐患分析:
    2.png 3.png
    • 解决办法,你需要加一把“锁”,看代码吧:
    // 将上边的错误代码中的这个方法加以修改就好了就是加了一个@synchronized(self)
     -  (void)sellTickets
    {
        while (1) {
         @synchronized(self){//只能加一把锁
    
            //1.先检查票数
            int count=self.leftTicketsCount;
            if (count>0) {
                //暂停一段时间
                [NSThread sleepForTimeInterval:0.002];
                //2.票数-1
                self.leftTicketsCount= count-1;
                //获取当前线程
                NSThread *current=[NSThread currentThread];
                NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);
            }else
            {
                //退出线程
                [NSThread exit];
            }
        }
        }
    }
    

    其他代码无需更改,再试下是不是就好了!保证每张票都是只被卖出一次。这个“锁”就是保证数据不发生错乱的根本保障。

    • 互斥锁的优缺点
      优点:能有效防止因多线程抢夺资源造成的数据安全问题
      缺点:需要消耗大量的CPU资源
    • 互斥锁的使用前提:多条线程抢夺同一块资源
      相关专业术语:线程同步,多条线程按顺序地执行任务
      互斥锁,就是使用了线程同步技术

    原子和非原子属性

    • OC在定义属性时有nonatomic和atomic两种选择
      atomic:原子属性,为setter方法加锁(默认就是atomic)
      nonatomic:非原子属性,不会为setter方法加锁
      atomic加锁原理
     @property (assign, atomic) int age;
     - (void)setAge:(int)age
     { 
      @synchronized(self) { 
        _age = age;
     }
     }
    
    • 原子和非原子属性的选择
      nonatomic和atomic对比
      atomic:线程安全,需要消耗大量的资源
      nonatomic:非线程安全,适合内存小的移动设备

    • iOS开发的建议
      所有属性都声明为nonatomic
      尽量避免多线程抢夺同一块资源
      尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

    相关文章

      网友评论

        本文标题:iOS开发多线程--线程安全

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