锁 🔒
锁用于解决多线程间资源共享的安全问题
分类:
- 自旋锁(Spinlock): 即忙等,类似 do-while,会一直占着CPU、内存等即忙碌状态
- 互斥锁(Mutex): 闲等,即锁的线程处于休眠状态
- 非递归锁(non-recursive mutex)或称不可重入互斥锁(non-reentrant mutex): 同一时刻只能被一条线程所拥有
- 可递归锁(recursive mutex)或称可重入互斥锁(reentrant mutex): 同一时刻能被多条线程所拥有
- 读写锁: 多读单写、读和写要互斥,有空再补
iOS中常见方案:
OSSpinLock: 自旋锁
自iOS10移除,被os_unfair_lock替代,因为优先级反转(Priority inversion)问题,优先级高的线程会被系统持续优先调度,所以优先级低的线程一直在等待,无法执行任务
1
2
3
4OSSpinLock lock = OS_SPINLOCK_INIT; // 初始化
OSSpinLockLock(&lock); // 加锁
OSSpinLockUnlock(&lock); // 解锁
OSSpinLockTry(&lock); // 尝试上锁,false即失败,锁被其他线程持有
os_unfair_lock: 互斥锁
iOS10开始支持,用以替代不安全的OSSpinLock;
等待锁的线程会处于休眠状态,
1
2
3
4os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT; // 初始化
os_unfair_lock_lock(&unfairLock); // 加锁
os_unfair_lock_unlock(&unfairLock); // 解锁
os_unfair_lock_trylock(&unfairLock); // 尝试上锁
pthread_mutex: 互斥锁
线程在等待锁时会处于休眠状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//非递归
pthread_mutex_t lock0;
pthread_mutex_init(&lock0, NULL); // 初始化
pthread_mutex_lock(&lock0); // 加锁
// ...
pthread_mutex_unlock(&lock0); // 解锁
pthread_mutex_destroy(&lock0); // 释放
//递归
pthread_mutex_t lock1;
pthread_mutexattr_t attr; // 用于设置锁的类型
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 递归
pthread_mutex_init(&lock1, &attr); // 初始化
pthread_mutexattr_destroy(&attr); // 释放
pthread_mutex_lock(&lock1); // 加锁
// ...
pthread_mutex_unlock(&lock1); // 解锁
pthread_mutex_destroy(&lock1); // 释放
NSLock: 再熟悉不过的吧,遵循NSLocking协议
1 | NSLock *aLock = [[NSLock alloc] init]; |
NSCondition: 互斥锁,遵循NSLocking协议
存在虚假唤醒问题,
1 | - (void)testConditionLock { |
NSConditionLock:互斥锁,遵循NSLocking协议
1 | var conditionLock = [[NSConditionLock alloc] initWithCondition:10]; |
NSRecursiveLock: 可重入互斥锁,遵循NSLocking协议
递归锁,就是字面意思,可以递归加锁加锁加锁 再解锁解锁解锁的锁
1
2
3
4
5
6
7
8
9
10
11
12dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^recursiveBlock)(int);
recursiveBlock = ^(int value){
if (value > 0) {
[self.recursiveLock lock]; // 加锁
NSLog(@"value: %d", value);
recursiveBlock(value - 1);
[self.recursiveLock unlock]; // 解锁
}
};
recursiveBlock(10);
});
@synchronized: 可重入互斥锁,最简单的锁
1 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ |
读写锁:也称“共享-互斥锁”、多读者-单写者锁
读操作可并发重入,写操作是互斥的,而且读和写的操作也是互斥的。
同一时刻要么写的操作,要么读的操作。
若是写,则最多只能有一个线程在写;若是读,则可以多个线程同时进行读
1
2
3
4
5
6
7
8
9
10// 并发队列,因为需要多读
dispatch_queue_t queue = dispatch_queue_create("pushLock", DISPATCH_QUEUE_CONCURRENT);
// 读操作,同步可以直接获取结果
dispatch_sync(queue, ^{
// 读操作
});
// 写操作,栅栏函数可以保证此时只有写操作,异步可以避免耗时操作阻塞线程
dispatch_barrier_async(queue, ^{
// 写操作,可能耗时
});