在 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;
}
}
}
结果若下:
我们发现 居然出现了 票数 为 负数的情况!!!!!
如果我们把 [NSThread sleepForTimeInterval:1]; 这句话去掉 再次运行 就不会必然出现这种情况了。
这句话的作用是 我们人为的造成 争抢资源就是 多线程 同时访问 某个变量的 场景 。就好像大家都在同时按键盘抢票。 这个时候 数据是不正常的。不论我们 设置 属性是 atomic 还是 nonatomic 都会出现。
再次 假设 我们把 [NSThread sleepForTimeInterval:1] 替换上一个 耗时的代码 比如:
for (int i=0; i<1000;i++ ) {
for (int j=0; j<1000; j++) {
m++;
}
}
依然 出现 票数为 负数的情况 。 在多线程编程中, 很多开发者 很容易编写这样消耗时间的操作,过后很容易出现 数据 不正常的情况。