补充一下,总结的两张图,5类锁,9种Lock:
首先理解几个锁的概念:
互斥锁(mutexlock)sleep-waiting:
保证共享数据操作的完整性, 锁被占用的时候会休眠, 等待锁释放的时候会唤醒。
在访问共享资源之前进行加锁,访问完成后解锁。
加锁后,任何其他试图加锁的线程会被阻塞,直到当前线程解锁。
解锁时,如果有1个以上的线程阻塞,那么所有该锁上的线程变为就绪状态,第一个就绪的加锁,其他的又进入休眠。
从而实现在任意时刻,最多只有1个线程能够访问被互斥锁保护的资源。
自旋锁(spinlock)busy-waiting:
跟互斥类似, 只是资源被占用的时候, 会一直循环检测锁是否被释放(CPU不能做其他的事情)
节省了唤醒睡眠线程的内核消耗(在加锁时间短暂的情况下会大大提高效率)
读写锁(rwlock):
顾名思义 - 写的时候:读写都等待;读的时候:写等待,读无需等待
适合读次数远远大于写的情况 (为了公平,应该设计为:写操作到来时,后面的读阻塞)
条件锁(condlock):
线程会因为条件变量不满足而阻塞,线程也可以在释放锁时将条件变量改成某个值,从而唤醒满足条件变量的线程
递归锁(recursivelock):
跟互斥类似, 但是允许同一个线程在未释放锁前,加锁N次锁, 不会引发死锁
死锁:
这里给出的是同一个线程,在解锁之前,多次上锁的例子:会导致死锁。下面任意一个递归锁都可以解决这个导致死锁的问题。
代码语言:javascript复制NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"加锁 %d", value);
sleep(2);
RecursiveMethod(value - 1); // 解锁之前又加想锁, 需要等待锁的解除,
}
NSLog(@"解锁 %d", value);
[lock unlock];
};
RecursiveMethod(5);
});
接下来按目前的耗时排序介绍,先耗时短的:
1、OSSpinLock 自旋锁
会造成优先级反转的问题,iOS10 已废弃,有兴趣可以看看:不再去安全的OSSpinLock。这里也给出一下使用例子:
代码语言:javascript复制NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
__block OSSpinLock osslock = OS_SPINLOCK_INIT; // 初始化
for (int i = 0; i < items.count; i ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&osslock); // 加锁
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
OSSpinLockUnlock(&osslock); // 解锁
});
}
2、os_unfair_lock 互斥锁 iOS10
iOS10 才支持,为了代替OSSpinLock,需要: #import <os/lock.h>。使用例子:
代码语言:javascript复制for (int i = 0; i < items.count; i ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT); // 必须在线程里初始化
os_unfair_lock_lock(unfairLock); // 加锁
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"线程1 资源1: %@", self.items);
os_unfair_lock_unlock(unfairLock); // 解锁
});
}
3、dispatch_semaphore 信号量
保证关键代码不并发执行。使用例子:
代码语言:javascript复制NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < items.count; i ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 加锁
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
dispatch_semaphore_signal(semaphore); // 解锁
});
}
4、pthread_mutex 互斥锁
苹果做出了优化, 性能不比semaphore差, 而且肯定安全(它有两种初始化方法,第二种带参数的可以设置type,下面会介绍)。需要:#import <pthread.h>。使用例子:
代码语言:javascript复制pthread_mutex_t _pthread;
pthread_mutex_init(&_pthread, NULL); // _pthread 不能是局部变量!!
NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
for (int i = 0; i < items.count; i ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&_pthread); // 加锁
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
pthread_mutex_unlock(&_pthread); // 解锁
});
}
5、NSLock 互斥锁
很方便,竞争的是这个锁对象。使用例子:
代码语言:javascript复制self.lock = [[NSLock alloc] init];
self.lock.name = @"itemsLock";
NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
for (int i = 0; i < items.count; i ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.lock lock]; // 加锁
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
[self.lock unlock]; // 解锁
});
}
6、NSCondition 条件锁
内部有实现NSLocking协议, 就能实现NSLock所有功能。使用例子:
代码语言:javascript复制NSCondition *lock = [[NSCondition alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start 1");
[lock lock];
// [lock waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; // 让当前线程等待一段时间
[lock wait];
NSLog(@"end 1");
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start 2");
[lock lock];
[lock wait];
NSLog(@"end 2");
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start 3");
sleep(2);
// [lock signal]; // 唤醒一个等待的线程
[lock broadcast]; // 唤醒所有等待的线程
NSLog(@"end 3");
});
7、pthread_mutex(recursive) 递归锁
需要设置 type = PTHREAD_MUTEX_RECURSIVE。
代码语言:javascript复制pthread_mutex_t plock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); // 初始化attr, 并赋予默认
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 设置type为递归锁
pthread_mutex_init(&plock, &attr); // 若为 pthread_mutex_init(&plock, NULL) 则会死锁
pthread_mutexattr_destroy(&attr); // 销毁一个属性对象,在重新初始化之前该结构不能重复使用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
NSLog(@"加锁: %d", value);
pthread_mutex_lock(&plock); // 加锁
if (value < 5) {
sleep(2);
RecursiveMethod( value); // 解锁之前又加想锁, 需要等待锁的解除,
}
NSLog(@"解锁: %d", value);
pthread_mutex_unlock(&plock); // 解锁
};
RecursiveMethod(1);
NSLog(@"finish");
});
atrr的始终类型:
- PTHREAD_MUTEX_NORMAL: 互斥锁不会检测死锁。
当一个线程解锁之前又锁上,将导致死锁。
尝试解除其他线程上的锁,结果不可预测。
尝试解除一个未锁定的锁,结果不可预测。
- PTHREAD_MUTEX_ERRORCHECK: 互斥锁提供错误检查。
当一个线程尝试重新锁定一个还未解开的锁时,将会返回一个错误。
尝试解除其他线程上的锁,将会返回一个错误。
尝试解除一个未锁定的锁,将会返回一个错误。
- PTHREAD_MUTEX_RECURSIVE: 递归锁
一个线程可以多次锁定一个还未解开的锁,需要相同数量的解锁来释放锁,然后另一个线程才能获的互斥锁
尝试解除其他线程上的锁,将会返回一个错误。
尝试解除一个未锁定的锁,将会返回一个错误。
- PTHREAD_MUTEX_DEFAULT:
尝试递归锁定此类型的锁,结果不可预测。
尝试解除其他线程上的锁,结果不可预测。
尝试解除一个未锁定的锁,结果不可预测。
8、NSRecursiveLock 递归锁
直接操作lock对象很方便,竞争的是这个锁对象。使用例子:
代码语言:javascript复制NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
// [lock tryLock];
// [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:2]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
NSLog(@"加锁: %d", value);
[lock lock]; // 加锁
if (value < 5) {
sleep(2);
RecursiveMethod( value); // 解锁之前又锁上
}
NSLog(@"解锁: %d", value);
[lock unlock]; // 解锁
};
RecursiveMethod(1);
NSLog(@"finish");
});
9、NSConditionLock 条件锁
代码语言:javascript复制NSConditionLock *cLock = [[NSConditionLock alloc] initWithCondition:0]; // 设置初始标志位
// [cLock lock]; // 不区分条件, 可加锁就执行
// [cLock unlock]; // 不会清空条件, 之后满足条件的锁还会执行
// [cLock tryLockWhenCondition:n]; // 标志位为n时, 加锁成功
// [cLock unlockWithCondition:n]; // 解锁, 并设置标志位
// [cLock lockWhenCondition:n]; // 标志位为n时加锁, 执行之后代码
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([cLock tryLockWhenCondition:0]) {
NSLog(@"任务1");
[cLock unlockWithCondition:1];
} else {
NSLog(@"任务1 加锁失败");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lockWhenCondition:3];
NSLog(@"任务2");
[cLock unlockWithCondition:2];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lockWhenCondition:1];
NSLog(@"任务3");
[cLock unlockWithCondition:3];
});
10、@synchronized 递归锁
用pthread_mutex_t实现的。参数只是一个标识符,当多个异步线程传入的参数一致时会触发锁机制,当不一致时,则不会阻塞。
代码语言:javascript复制NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
for (int i = 0; i < items.count; i ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (self.items) { // 加锁
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
}
});
}
11、POSIXConditions 条件锁:互斥锁 条件锁
参考 线程被一个 互斥 和 条件 结合的信号来唤醒,当锁和条件同时满足时,才能执行:
代码语言:javascript复制- (void)POSIX_Codictions {
ready_to_go = false;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&condition, NULL);
[self waitOnConditionFunction];
[self signalThreadUsingCondition];
}
- (void)waitOnConditionFunction {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex); // Lock the mutex.
while(ready_to_go == false) {
NSLog(@"wait...");
pthread_cond_wait(&condition, &mutex); // 休眠
}
NSLog(@"done");
ready_to_go = false;
pthread_mutex_unlock(&mutex);
});
}
- (void)signalThreadUsingCondition {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex); // Lock the mutex.
ready_to_go = true;
NSLog(@"true");
pthread_cond_signal(&condition); // Signal the other thread to begin work.
pthread_mutex_unlock(&mutex);
});
}
12、pthreadReadWrite 读写锁
代码语言:javascript复制__block pthread_rwlock_t rwLock;
pthread_rwlock_init(&rwLock, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_rwlock_wrlock(&rwLock);
NSLog(@"3 写 begin");
sleep(3);
NSLog(@"3 写 end");
pthread_rwlock_unlock(&rwLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_rwlock_rdlock(&rwLock);
NSLog(@"1 读 begin");
sleep(1);
NSLog(@"1 读 end");
pthread_rwlock_unlock(&rwLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_rwlock_rdlock(&rwLock);
NSLog(@"2 读 begin");
sleep(2);
NSLog(@"2 读 end");
pthread_rwlock_unlock(&rwLock);
});
Demo github地址