在 iOS 中我们经常需要使用到 多线程 ,那么多线程 使用中有个问题 需要注意就是 多线程访问数据的安全。我们举例说明

现在 controller 中有两个属性。 对每个属性 我们分别使3个多线程 去读写 这两个属性

/*

 nonatomic  setter getter 都不加锁

 */

@property (nonatomic,assign) NSInteger leftTicketsCount;


/**

 使用atomic多线程原子性控制,atomic的原理给setter加上锁,getter不会加锁

 */

@property (atomic,assign) NSInteger newLefTicketsCount;


代码如下

#pragma mark ---- Thread fighting over shampo

/**

 多线程 争抢 资源  atomic  所谓争抢 就是几乎 同时访问的意思

 */

- (void)fightingOverShampo

{

    self.leftTicketsCount=10;

    

    //开启多个线程,模拟售票员售票

    self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];

    self.thread1.name=@"售票员1";

    self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];

    self.thread2.name=@"售票员2";

    self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];

    self.thread3.name=@"售票员3";

    

    [self.thread1 start];

    [self.thread2 start];

    [self.thread3 start];


}


- (void)sellTickets

{

    while (true) {

        // 从代码来看应该 应该不可能出现 负数的 票数

//        NSLog(@"start ticket %ld---",(long)self.leftTicketsCount);

        if (self.leftTicketsCount>0) { // 读取

            

            NSLog(@"valid ticke %ld",self.leftTicketsCount);

            [NSThread sleepForTimeInterval:1]; // 这句话 是关键 睡眠 执行 笔者认为 如果

            self.leftTicketsCount--;

            NSLog(@"thread:%@ ----> %ld",[[NSThread currentThread] name],self.leftTicketsCount);

        }else{

           

            NSLog(@"break thread:%@ ----> %ld",[[NSThread currentThread] name],self.leftTicketsCount);

            break;

        }

    

    }

}



#pragma mark ---- 第二个 属性 对比 

- (void)fightingOverShampoTwo

{

    self.newLefTicketsCount=10;

    

    //开启多个线程,模拟售票员售票

    self.thread4=[[NSThread alloc]initWithTarget:self selector:@selector(atomicSellTicket) object:nil];

    self.thread4.name=@"售票员4";

    

    self.thread5=[[NSThread alloc]initWithTarget:self selector:@selector(atomicSellTicket) object:nil];

    self.thread5.name=@"售票员5";

    

    self.thread6=[[NSThread alloc]initWithTarget:self selector:@selector(atomicSellTicket) object:nil];

    self.thread6.name=@"售票员6";

    

    [self.thread4 start];

    [self.thread5 start];

    [self.thread6 start];

}



- (void)atomicSellTicket

{

    while (true) {

        // 从代码来看应该 应该不可能出现 负数的 票数

        //        NSLog(@"start ticket %ld---",(long)self.leftTicketsCount);

        if (self.newLefTicketsCount>0) { // 读取

            

//            NSLog(@"valid ticke %ld",self.newLefTicketsCount);

            [NSThread sleepForTimeInterval:1]; // 这句话 是关键 睡眠 执行 笔者认为 如果

            self.newLefTicketsCount--;

            NSLog(@"new  thread:%@ ----> %ld",[[NSThread currentThread] name],self.newLefTicketsCount);

        }else{

            

//            NSLog(@"break thread:%@ ----> %ld",[[NSThread currentThread] name],self.leftTicketsCount);

            break;

        }

        

    }

}


结果若下:

iOS 多线程安全之产生原因iOS 多线程安全之产生原因

我们发现 居然出现了 票数 为 负数的情况!!!!! 

如果我们把  [NSThread sleepForTimeInterval:1]; 这句话去掉 再次运行 就不会必然出现这种情况了。

这句话的作用是 我们人为的造成 争抢资源就是 多线程 同时访问 某个变量的 场景 。就好像大家都在同时按键盘抢票。 这个时候 数据是不正常的。不论我们 设置 属性是 atomic 还是 nonatomic 都会出现。

再次 假设 我们把 [NSThread sleepForTimeInterval:1] 替换上一个 耗时的代码 比如:

            for (int i=0; i<1000;i++ ) {

                for (int j=0; j<1000; j++) {

                    m++;

                }

            }


依然 出现 票数为 负数的情况  。  在多线程编程中, 很多开发者 很容易编写这样消耗时间的操作,过后很容易出现 数据 不正常的情况。





相关文章:

  • 2021-07-13
  • 2021-12-27
  • 2022-12-23
  • 2021-10-31
  • 2021-09-25
猜你喜欢
  • 2021-12-04
  • 2021-09-14
  • 2021-07-29
  • 2022-01-11
  • 2022-12-23
  • 2021-09-05
  • 2022-12-23
相关资源
相似解决方案