ios-多线程隐患

186 阅读3分钟

ios-多线程隐患

多线程会造成一定的安全隐患:

资源共享

  • 一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源

  • 比如多个线程访问同一个对象、同一个变量、同一个文件

  • 当多个线程访问同一块资源师,很容易引发数据错乱和数据安全问题

    多线程安全隐患

示例01-存钱取钱

image-20220423223928355

由于两条线程同时对余额进行修改,导致余额的值不准确了。

示例02-卖票

image-20220423224148874

由于两条线程都在对票数进行修改,导致这个票实际卖出去两张,余票显示只卖出去一张!

代码示例--卖票

- (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];
        }
    });
}

image-20220425224348415

从打印的结果来看 我们可以看出来原本有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]);
}

image-20220425225442081

从打印结果看出我们原有100元我们存了5次钱取了5次钱,正确答案应该是250元才对,事实上最终结果是220元,钱凭空少了一些。

这就是多线程给我们带来的隐患。

我们分析下为什么会出现这个问题:

image-20220425225950307

上图可以看出在最开始的时候大家拿到的值都是一样的,线程之间没有顺序,且互不通信,随着时间线的推移大家在自己的线程分别进行了一次+1的操作,然后各自写入导致最终的结果不对了事实上应该是19才对。

解决方案

使用线程同步技术(同步,就是协同步调,按预定的先后次序进行运行)

常见的线程同步技术就是:加锁

image-20220425230726416

上图可以看出访问数据必须按照预定好的顺序,并且同一时间只有一条线程可以访问

  • 加锁的目的是为了当前访问的只有我这一条线程
  • 加完锁必须要解锁,否则会导致其他人再也无法访问这块数据了

下文会介绍ios中的线程同步方案,敬请期待!

ps:感谢小码哥的课程