锁 🔒
锁用于解决多线程间资源共享的安全问题
分类:
- 自旋锁(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, ^{
 // 写操作,可能耗时
 });
