ios-多线程隐患
多线程会造成一定的安全隐患:
资源共享
-
一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
-
比如多个线程访问同一个对象、同一个变量、同一个文件
-
当多个线程访问同一块资源师,很容易引发数据错乱和数据安全问题
多线程安全隐患
示例01-存钱取钱
由于两条线程同时对余额进行修改,导致余额的值不准确了。
示例02-卖票
由于两条线程都在对票数进行修改,导致这个票实际卖出去两张,余票显示只卖出去一张!
代码示例--卖票
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self saleTickets];
}
- (void)saleTicket{
int oldTicketsCount = self.ticketsCount;
sleep(.2);//为了更加凸显多线程带来的隐患
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
// self.ticketsCount--;//该句代码等同于上几句代码
NSLog(@"还剩%d张票 - %@",oldTicketsCount,[NSThread currentThread]);
}
- (void)saleTickets {
self.ticketsCount = 15;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
}
从打印的结果来看 我们可以看出来原本有15张票,卖了15次,结果还剩下4张票
代码示例-存钱取钱
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// [self ticketTest];
[self moneyTest];
}
/**
存钱取钱演示
*/
- (void)moneyTest{
self.money = 100;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saveMoney];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self drawMoney];
}
});
}
/**
存钱
*/
- (void)saveMoney{
int oldMoney = self.money;
sleep(.2);//为了更加凸显多线程带来的隐患
oldMoney += 50;
self.money = oldMoney;
NSLog(@"存50元还剩%d元 - %@",oldMoney,[NSThread currentThread]);
}
/**
取钱
*/
- (void)drawMoney{
int oldMoney = self.money;
sleep(.2);//为了更加凸显多线程带来的隐患
oldMoney -= 20;
self.money = oldMoney;
NSLog(@"取20元还剩%d元 - %@",oldMoney,[NSThread currentThread]);
}
从打印结果看出我们原有100元我们存了5次钱取了5次钱,正确答案应该是250元才对,事实上最终结果是220元,钱凭空少了一些。
这就是多线程给我们带来的隐患。
我们分析下为什么会出现这个问题:
上图可以看出在最开始的时候大家拿到的值都是一样的,线程之间没有顺序,且互不通信,随着时间线的推移大家在自己的线程分别进行了一次+1的操作,然后各自写入导致最终的结果不对了事实上应该是19才对。
解决方案
使用线程同步技术(同步,就是协同步调,按预定的先后次序进行运行)
常见的线程同步技术就是:加锁
上图可以看出访问数据必须按照预定好的顺序,并且同一时间只有一条线程可以访问
- 加锁的目的是为了当前访问的只有我这一条线程
- 加完锁必须要解锁,否则会导致其他人再也无法访问这块数据了
下文会介绍ios中的线程同步方案,敬请期待!
ps:感谢小码哥的课程