iOS技术图谱之多线程安全问题
2019-11-15 本文已影响0人
iOS大蝠
多线程安全问题
多个线程访问同一块资源进行读写,如果不加控制随意访问容易产生数据错乱从而引发数据安全问题。为了解决这一问题,就有了加锁的概念。加锁的原理就是当有一个线程正在访问资源进行写的时候,不允许其他线程再访问该资源,只有当该线程访问结束后,其他线程才能按顺序进行访问。对于读取数据,有些程序设计是允许多线程同时读的,有些不允许。UIKit中几乎所有控件都不是线程安全的,因此需要在主线程上更新UI。
为了解决多线程访问安全问题,iOS提供了以下几种方式来通过加锁保证多个线程访问共享资源(临界区)安全。
(1)@synchronized
(2)NSLock
(3)NSConditionLock
(4)NSRecursiveLock
以上4种加锁方式我们通过例子来一一介绍。
@synchronized
self.tickets = 200;
NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
thread1.name = @"售票员1";
NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
thread2.name = @"售票员2";
[thread1 start];
[thread2 start];
- (void)saleTickets{
while (YES) {
[NSThread sleepForTimeInterval:2];
//互斥锁 -- 保证锁内的代码在同一时间内只有一个线程在执行
@synchronized (self) {
NSLog(@"%@ -- 开始卖票",[NSThread currentThread].name);
if (self.tickets>0) {
self.tickets--;
NSLog(@"剩余票数 = %ld",self.tickets);
}else{
NSLog(@"票卖完了");
break;
}
}
}
}
通过互斥锁,我们可以看到,它保证了同一时间只有一个线程来访问临界区,这样self.ticket的值可以保证安全。
NSLock
线程代码一样
执行代码我们使用NSLock
- (void)saleTickets{
while (YES) {
[NSThread sleepForTimeInterval:2];
// @synchronized (self) {
[self.lock lock];
NSLog(@"%@ -- 开始卖票",[NSThread currentThread].name);
if (self.tickets>0) {
self.tickets--;
NSLog(@"剩余票数 = %ld",self.tickets);
}else{
NSLog(@"票卖完了");
break;
}
// }
[self.lock unlock];
}
}
NSConditionLock(条件锁)
使用此锁,在线程没有获得锁的情况下,阻塞,即暂停运行,典型用于生产者/消费者模型。
- (instancetype)initWithCondition:(NSInteger)condition;//初始化条件锁
- (void)lockWhenCondition:(NSInteger)condition;//加锁 (条件是:锁空闲,即没被占用;条件成立)
- (BOOL)tryLock; //尝试加锁,成功返回TRUE,失败返回FALSE
- (BOOL)tryLockWhenCondition:(NSInteger)condition;//在指定条件成立的情况下尝试加锁,成功返回TRUE,失败返回FALSE
- (void)unlockWithCondition:(NSInteger)condition;//在指定的条件成立时,解锁
- (BOOL)lockBeforeDate:(NSDate *)limit;//在指定时间前加锁,成功返回TRUE,失败返回FALSE,
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;//条件成立的情况下,在指定时间前加锁,成功返回TRUE,失败返回FALSE,
@property (readonly) NSInteger condition;//条件锁的条件
@property (nullable, copy) NSString *name;//条件锁的名称
聚个例子:
NSConditionLock* myCondition=[[NSConditionLock alloc]init];
[NSThread detachNewThreadWithBlock:^{
for(int i=0;i<5;i++)
{
[myCondition lock];
NSLog(@"当前解锁条件:%d",i);
sleep(2);
[myCondition unlockWithCondition:i];
BOOL isLocked=[myCondition tryLockWhenCondition:2];
if(isLocked)
{
NSLog(@"加锁成功!!!!!");
[myCondition unlock];
}
}
}];
NSRecursiveLock(递归锁)
此锁可以在同一线程中多次被使用,但要保证加锁与解锁使用平衡,多用于递归函数,防止死锁。
- (BOOL)tryLock;//尝试加锁,成功返回TRUE,失败返回FALSE
- (BOOL)lockBeforeDate:(NSDate *)limit;//在指定时间前尝试加锁,成功返回TRUE,失败返回FALSE
@property (nullable, copy) NSString *name;//线程锁名称
举个例子:
-(void)initRecycle:(int)value
{
[myRecursive lock];
if(value>0)
{
NSLog(@"当前的value值:%d",value);
sleep(2);
[self initRecycle:value-1];
}
[myRecursive unlock];
}