1、程安全隐患
当多个线程同时访问一块资源时,很容易产生数据错乱和数据安全问题,比如多个线程读取/写入同一个对象、同一个变量、同一个文件等。
2、问题举例
2.1、存钱取钱问题
当多条线程同时进行存钱、取钱行为,就会出现余额不正确的问题,如图所示:
- (void)moneyTest {self.money = 500;dispatch_queue_t queue = dispatch_get_global_queue(0, 0);// 每次存入50,存10次,共500快dispatch_async(queue, ^{for (int i = 0; i < 10; i++) {[self saveMoney];}});// 每次取出20,存10次,共200快dispatch_async(queue, ^{for (int i = 0; i < 10; i++) {[self drawMoney];}});}- (void)saveMoney {int oldMoney = self.money;sleep(.2);oldMoney += 50;self.money = oldMoney;NSLog(@"存入50还剩%d元",self.money);}- (void)drawMoney {int oldMoney = self.money;sleep(.2);oldMoney -= 20;self.money = oldMoney;NSLog(@"取出20还剩%d元",self.money);}
打印结果:
~: 取出20还剩480元~: 存入50还剩550元~: 取出20还剩530元~: 存入50还剩580元~: 取出20还剩560元~: 存入50还剩610元~: 取出20还剩590元~: 存入50还剩640元~: 取出20还剩620元~: 存入50还剩670元~: 取出20还剩650元~: 存入50还剩700元~: 取出20还剩680元~: 存入50还剩730元~: 取出20还剩710元~: 存入50还剩760元~: 取出20还剩740元~: 存入50还剩790元~: 取出20还剩770元~: 存入50还剩820元
期望剩余应该是 500 + 500 - 200 = 800元。
2.2、卖票问题
当多条线程同时进行卖票操作时,就会出现余票不正确问题,如图所示:
- (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];}});}- (void)saleTicket {int oldTicketsCount = self.ticketsCount;sleep(.2);oldTicketsCount--;self.ticketsCount = oldTicketsCount;NSLog(@"剩余%d张票", self.ticketsCount);}
打印结果:
~: 剩余14张票~: 剩余13张票~: 剩余14张票~: 剩余12张票~: 剩余12张票~: 剩余12张票~: 剩余11张票~: 剩余10张票~: 剩余9张票~: 剩余8张票~: 剩余7张票~: 剩余6张票~: 剩余5张票~: 剩余4张票~: 剩余3张票
3、解决方案
使用线程同步技术(同步,就是协同步调,按预定的先后次序进行),常见的线程同步技术是:加锁
比如上面提到的存钱取钱问题,在存钱之前先加锁,保证数据只有当前线程可以访问,在存钱结束时解锁。这时取钱线程访问数据,也进行加锁解锁操作,这样就能保证数据读写的安全性。
