iOS 多线程安全之产生原因

来源:互联网 发布:sai软件入门教程 编辑:程序博客网 时间:2024/05/22 15:08

在 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=[[NSThreadalloc]initWithTarget:selfselector:@selector(sellTickets)object:nil];

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

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

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

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

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

    

    [self.thread1start];

    [self.thread2start];

    [self.thread3start];


}


- (void)sellTickets

{

    while (true) {

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

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

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

            

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

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

            self.leftTicketsCount--;

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

        }else{

           

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

            break;

        }

    

    }

}



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

- (void)fightingOverShampoTwo

{

    self.newLefTicketsCount=10;

    

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

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

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

    

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

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

    

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

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

    

    [self.thread4start];

    [self.thread5start];

    [self.thread6start];

}



- (void)atomicSellTicket

{

    while (true) {

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

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

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

            

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

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

            self.newLefTicketsCount--;

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

        }else{

            

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

            break;

        }

        

    }

}


结果若下:


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

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

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

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

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

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

                    m++;

                }

            }


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