iOS开发中知道的哪些锁? 哪个性能最差?
锁是线程编程同步工具的基础。iOS开发中常用的锁有如下几种:
@synchronized
NSLock 对象锁
NSRecursiveLock 递归锁
NSConditionLock 条件锁
pthread_mutex 互斥锁(C语言)
dispatch_semaphore 信号量实现加锁(GCD)
OSSpinLock 参考YY
- @synchronized 关键字加锁 互斥锁,性能较差不推荐使用
@synchronized(这里添加一个OC对象,一般使用self) {
这里写要加锁的代码
}
注意点
1.加锁的代码尽量少
2.添加的OC对象必须在多个线程中都是同一对象
3.优点是不需要显式的创建锁对象,便可以实现锁的机制。
4. @synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。
//设置票的数量为5
_tickets = 5;
//线程1
dispatch_async(dispatch_get_main_queue(), ^{
[self saleTickets];
});
//线程2
dispatch_async(dispatch_get_main_queue(), ^{
[self saleTickets];
});
- (void)saleTickets
{
while (1) {
@synchronized(self) {
[NSThread sleepForTimeInterval:1];
if (_tickets > 0) {
_tickets--;
NSLog(@"剩余票数= %ld, Thread:%@",_tickets,[NSThread currentThread]);
} else {
NSLog(@"票卖完了 Thread:%@",[NSThread currentThread]);
break;
}
}
}
}
- NSLock
//加锁
- (void)lock;
//解锁
- (void)unlock;
在Cocoa程序中NSLock中实现了一个简单的互斥锁。
所有锁(包括NSLock)的接口实际上都是通过NSLocking协议定义的,它定义了lock和unlock方法。你使用这些方法来获取和释放该锁。
NSLock类还增加了tryLock和lockBeforeDate:方法。
tryLock试图获取一个锁,但是如果锁不可用的时候,它不会阻塞线程,相反,它只是返回NO。
lockBeforeDate:方法试图获取一个锁,但是如果锁没有在规定的时间内被获得,它会让线程从阻塞状态变为非阻塞状态(或者返回NO)。
还是卖票的例子
- (void)viewDidLoad {
[super viewDidLoad];
//设置票的数量为5
_tickets = 5;
_mutexLock = [[NSLock alloc]init];
//线程1
dispatch_async(dispatch_get_main_queue(), ^{
[self saleTickets];
});
//线程2
dispatch_async(dispatch_get_main_queue(), ^{
[self saleTickets];
});
}
- (void)saleTickets
{
while (1) {
[NSThread sleepForTimeInterval:1];
//加锁
[_mutexLock lock];
if (_tickets > 0) {
_tickets--;
NSLog(@"剩余票数= %ld, Thread:%@",_tickets,[NSThread currentThread]);
} else {
NSLog(@"票卖完了 Thread:%@",[NSThread currentThread]);
break;
}
//解锁
[_mutexLock unlock];
}
}
进入else 之后直接break退出循环,处于死锁状态
将方法改成如下能够解决死锁问题
- (void)saleTickets
{
while (1) {
[NSThread sleepForTimeInterval:1];
//加锁
[_mutexLock lock];
if (_tickets > 0) {
_tickets--;
NSLog(@"剩余票数= %ld, Thread:%@",_tickets,[NSThread currentThread]);
} else {
NSLog(@"票卖完了 Thread:%@",[NSThread currentThread]);
[_mutexLock unlock];
break;
}
//解锁
[_mutexLock unlock];
}
}
//创建锁
_mutexLock = [[NSLock alloc]init];
//线程1
dispatch_async(dispatch_get_main_queue(), ^{
static void(^TestMethod)(int);
TestMethod = ^(int value)
{
[_mutexLock lock];
if (value > 0)
{
[NSThread sleepForTimeInterval:1];
TestMethod(value--);
}
[_mutexLock unlock];
};
TestMethod(5);
});
在递归中使用NSLock 导致死锁
在这种情况下,我们就可以使用NSRecursiveLock。它可以允许同一线程多次加锁,而不会造成死锁。递归锁会跟踪它被lock的次数。每次成功的lock都必须平衡调用unlock操作。只有所有达到这种平衡,锁最后才能被释放,以供其它线程使用。
//创建锁
_rsLock = [[NSRecursiveLock alloc] init];
//线程1
dispatch_async(self.concurrentQueue, ^{
static void(^TestMethod)(int);
TestMethod = ^(int value)
{
[_rsLock lock];
if (value > 0)
{
[NSThread sleepForTimeInterval:1];
TestMethod(value--);
}
[_rsLock unlock];
};
TestMethod(5);
});
使用如上代码即可解决递归加锁
- NSConditionLock 条件锁
//主线程中
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lockWhenCondition:1];
NSLog(@"线程1");
sleep(2);
[lock unlock];
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保证让线程2的代码后执行
if ([lock tryLockWhenCondition:0]) {
NSLog(@"线程2");
[lock unlockWithCondition:2];
NSLog(@"线程2解锁成功");
} else {
NSLog(@"线程2尝试加锁失败");
}
});
//线程3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);//以保证让线程2的代码后执行
if ([lock tryLockWhenCondition:2]) {
NSLog(@"线程3");
[lock unlock];
NSLog(@"线程3解锁成功");
} else {
NSLog(@"线程3尝试加锁失败");
}
});
//线程4
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(3);//以保证让线程2的代码后执行
if ([lock tryLockWhenCondition:2]) {
NSLog(@"线程4");
[lock unlockWithCondition:1];
NSLog(@"线程4解锁成功");
} else {
NSLog(@"线程4尝试加锁失败");
}
});
NSConditionLock也跟其它的锁一样,是需要lock与unlock对应的,只是lock,lockWhenCondition:与unlock,unlockWithCondition:是可以随意组合的
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
- pthread_mutex 互斥锁
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
//线程1
dispatch_async(self.concurrentQueue), ^{
pthread_mutex_lock(&mutex);
NSLog(@"任务1");
sleep(2);
pthread_mutex_unlock(&mutex);
});
//线程2
dispatch_async(self.concurrentQueue), ^{
sleep(1);
pthread_mutex_lock(&mutex);
NSLog(@"任务2");
pthread_mutex_unlock(&mutex);
});
- dispatch_semaphore 信号量实现加锁
// 创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务1");
sleep(10);
dispatch_semaphore_signal(semaphore);
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务2");
dispatch_semaphore_signal(semaphore);
});