OC总结 - 锁
什么是线程安全?
多线程操作共享数据的时候不会出现意想不到的结果就叫线程安全,否则就是线程不安全。
原子属性是线程安全的吗?
原子属性只能保障set 或者 get的读写安全,但我们在使用属性的时候,往往既有set又有get,所以 说原子属性并不是线程安全的。
自旋锁 & 互斥锁
自旋锁: 在访问被锁的资源的时候,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释 放锁。(忙等)
互斥锁: 在访问被锁资源时,调用者线程会休眠,此时cpu可以调度其他线程工作。直到被锁的资 源释放锁。然后再唤醒休眠线程。(闲等)
自旋锁的优点在于,因为自旋锁不会引起调用者线程休眠,所以不会进行线程调度,cpu时间片轮 转等一些耗时的操作。所以如果能在很短的时间内获得锁,自旋锁的效率远高于互斥锁。
自旋锁缺点在于,自旋锁一直占用CPU,在未获得锁的情况下,一直自旋,相当于死循环,会一直 占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。 而且自旋锁不能实现递归调用 。
自旋锁优先级反转的bug
当多个线程有优先级的时候,如果一个优先级低的线程先去访问某个数据,此时使用自旋锁进行了 加锁,然后一个优先级高的线程又去访问这个数据,那么优先级高的线程因为优先级高会一直占着 CPU资源,此时优先级低的线程无法与优先级高的线程争夺 CPU 时间,从而导致任务迟迟完不 成、锁无法释放。
由于自旋锁本身存在的这个问题,所以苹果在 iOS 10 以后已经废弃了 OSSpinLock
。 也就是说除非大家能保证访问锁的线程全部都处于同一优先级,否则 iOS 系统中的自旋锁就不要去使用了.
锁
OSSpinLock
自旋锁
iOS 10 后弃用, 之后使用 os_unfair_lock
// 创建
OSSpinLock lock = OS_SPINLOCK_INIT;
// 加锁
OSSpinLockLock(&lock);
// 尝试加锁,返回 bool 值表示,true表示加锁成功,false表示加锁失败,锁正在被其他线程持有
BOOL s = OSSpinLockTry(&lock);
// 解锁
OSSpinLockUnlock(&lock);
os_unfair_lock
互斥锁
作为 OSSpinLock
弃用后的替代出现
使用时需要导入头文件 #import <os/lock.h>
// 创建
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
// 加锁
os_unfair_lock_lock(&lock);
// 尝试加锁,返回 bool 值表示,true表示加锁成功,false表示加锁失败,锁正在被其他线程持有
BOOL s = os_unfair_lock_trylock(&lock);
// 解锁
os_unfair_lock_unlock(&lock);
// 如果当前线程为持有指定的锁或者锁已被解锁,则触发崩溃
os_unfair_lock_assert_owner(&lock);
// 如果当前线程持有指定的锁,则触发崩溃
os_unfair_lock_assert_not_owner(&lock);
NSLock
互斥锁
非递归锁,不能重复加锁,否则会死锁
// 创建
NSLock *lock = [NSLock new];
// 加锁
[lock lock];
// 解锁
[lock unlock];
// 尝试加锁
BOOL s = [lock tryLock];
NSCondition 条件锁
互斥锁
非递归锁,不能重复加锁,否则会死锁
当线程之间存在数据相互依赖的情况下,它能起到很好的协调作用。
// 创建
NSCondition *lock = [NSCondition new];
// 加锁
[lock lock];
// 等待。阻塞当前线程,使线程进入休眠,等待唤醒信号
[lock wait];
// 唤醒。唤醒一个正在休眠的线程,如果要唤醒多个,需要调用多次。如果没有线程在等待,则什么也不做
[lock signal];
// 唤醒所有在等待的线程。如果没有线程在等待,则什么也不做
[lock broadcast];
// 解锁
[lock unlock];
虚假唤醒
当线程从等待已发出信号的条件变量中醒来,却发现它等待的条件不满足时,就会发生虚假唤醒。 之所以称为虚假,是因为该线程似乎无缘无故地被唤醒了。但是虚假唤醒不会无缘无故发生:它们 通常是因为在发出条件变量信号和等待线程最终运行之间,另一个线程运行并更改了条件。
在许多系统上,尤其是多处理器系统上,虚假唤醒的问题更加严重,因为如果有多个线程在条件变 量发出信号时等待它,系统可能会决定将它们全部唤醒,将每个 signal()
唤醒一个线程视为 broadcast()
唤醒所有,从而打破了信号和唤醒之间任何可能预期的 1:1 关系。如果有 10 个线 程在等待,那么只有一个会获胜,另外 9 个会经历虚假唤醒。
使用示例
for (int i = 0; i < 100; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.iCondition lock];
self.count ++;
NSLog(@"生产了一个产品,现有产品 : %d个",self.count);
[self.iCondition signal];
[self.iCondition unlock];
});
}
for (int i = 0; i < 100; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.iCondition lock];
// 使用 while 解决 虚假唤醒 问题
// 如果产品 为 0 ,无法消费,将一直等待产品生产出来
while (self.count == 0) {
[self.iCondition wait];
}
self.count --;
NSLog(@"消费了一个产品,现有产品: %d个",self.count);
[self.iCondition unlock];
});
}
NSConditionLock 条件锁
互斥锁
NSConditionLock
是对 NSCondition
的二次封装,自带条件判断,可用来控制线程执行顺序
// 创建 ,初始条件为 1 的 锁
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:1];
// 加锁
[lock lock];
// 解锁
[lock unlock];
// 加锁,只有当 锁的条件 == 1时,才会执行锁后面的代码
[lock lockWhenCondition:1];
// 解锁,并改变条件值 == 2
[lock unlockWithCondition:2];
使用示例
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:3];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[lock lockWhenCondition:3];
NSLog(@"线程 1");
[lock unlockWithCondition:2];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[lock lockWhenCondition:1];
NSLog(@"线程 3");
[lock unlockWithCondition:0];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[lock lockWhenCondition:2];
NSLog(@"线程 2");
[lock unlockWithCondition:1];
});
/*
打印结果一定是:
线程 1
线程 2
线程 3
执行顺序
初始条件 == 3,
执行[lock lockWhenCondition:3]后代码
打印“线程 1”
然后[lock unlockWithCondition:2];, 使得 条件 = 2
条件 == 2,
执行[lock lockWhenCondition:2] 后代码
打印“线程 2”
然后[lock unlockWithCondition:1];, 使得 条件 = 1
条件 == 1,
执行[lock lockWhenCondition:1] 后代码
打印“线程 3”
然后[lock unlockWithCondition:0];, 使得 条件 = 0
*/
NSRecursiveLock 递归锁
互斥锁
在 同一线程 中,支持递归调用。
多线程中还是可能产生死锁。递归锁可用多次加锁,可能导致多个条线程拥有这把锁,而解锁的条件是只有当前线程拥有这把锁,如果有多个线程持有了,就可能相互等待,导致死锁。
// 创建
NSRecursiveLock *lock = [NSRecursiveLock new];
// 加锁
[lock lock];
// 解锁
[lock unlock];
// 尝试加锁
BOOL s = [lock tryLock];
示例
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^recursiveMethod)(int);
recursiveMethod = ^(int value){
if (value > 0) {
[self.iRecursiveLock lock];
NSLog(@"%d",value);
recursiveMethod(value - 1);
[self.iRecursiveLock unlock];
}
};
recursiveMethod(10);
});
pthread_mutex
大部分的锁都是对 pthread_mutex
的封装
// 初始化锁,pthread_mutexattr_t可用来设置锁的类型。
pthread_mutex_init(pthread_mutex_t mutex,const pthread_mutexattr_t attr)
//加锁
pthread_mutex_lock(pthread_mutex_t mutex);
//尝试加锁,当锁已经在使用的时候,返回为EBUSY,而不是挂起等待,成功返回0.失败返回错误信息
pthread_mutex_trylock(*pthread_mutex_t *mutex);
//释放锁
pthread_mutex_unlock(pthread_mutex_t *mutex);
//使用完锁之后释放锁
pthread_mutex_destroy(pthread_mutex_t* mutex);
//设置互斥锁的范围
pthread_mutexattr_setpshared();
//获取互斥锁的范围
pthread_mutexattr_getpshared();
dispatch_semaphore_t 信号量
// 这个函数是创建一个dispatch_semaphore_t类型的 信号量,并且创建的时候需要指定信号量的大小
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
// 发送信号量。该函数会对信号量的值进行 +1 操作
dispatch_semaphore_signal(sem);
// 等待信 号量。如果信号量值 == 0,那么该函数就会一直等待,也就是不返回(相当于阻塞当前线程),
// 直到该函数等待的信号量的值 >= 1,该函数会对信号量的值进行 -1 操作,然后返回。
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
@synchronized 同步锁
互斥锁
递归锁,支持多线程加锁
@synchronized(nil) 时锁不会生效
各种锁加锁解释锁耗时对比
注意: 这只是一次运行的结果,每次运行耗时不同,只能表示个大概。
---------- 运行 (1000) 次 ----------
OSSpinLock: 0.15 ms
dispatch_semaphore: 0.17 ms
pthread_mutex: 0.19 ms
os_unfair_look: 0.19 ms
NSCondition: 0.40 ms
NSLock: 0.38 ms
pthread_mutex(recursive): 0.34 ms
NSRecursiveLock: 0.58 ms
NSConditionLock: 1.45 ms
@synchronized: 0.93 ms
---------- 运行 (10000) 次 ----------
OSSpinLock: 0.82 ms
dispatch_semaphore: 0.87 ms
pthread_mutex: 1.39 ms
os_unfair_look: 1.12 ms
NSCondition: 2.22 ms
NSLock: 2.23 ms
pthread_mutex(recursive): 2.06 ms
NSRecursiveLock: 3.33 ms
NSConditionLock: 6.91 ms
@synchronized: 4.49 ms
---------- 运行 (100000) 次 ----------
OSSpinLock: 15.83 ms
dispatch_semaphore: 9.24 ms
pthread_mutex: 10.70 ms
os_unfair_look: 10.43 ms
NSCondition: 15.88 ms
NSLock: 14.04 ms
pthread_mutex(recursive): 12.60 ms
NSRecursiveLock: 5.53 ms
NSConditionLock: 11.49 ms
@synchronized: 12.52 ms
---------- 运行 (1000000) 次 ----------
OSSpinLock: 77.15 ms
dispatch_semaphore: 59.11 ms
pthread_mutex: 43.83 ms
os_unfair_look: 35.18 ms
NSCondition: 50.60 ms
NSLock: 42.96 ms
pthread_mutex(recursive): 56.78 ms
NSRecursiveLock: 59.84 ms
NSConditionLock: 117.55 ms
@synchronized: 129.87 ms
---------- 运行 (10000000) 次 ----------
OSSpinLock: 301.97 ms
dispatch_semaphore: 323.77 ms
pthread_mutex: 376.66 ms
os_unfair_look: 374.91 ms
NSCondition: 347.86 ms
NSLock: 363.72 ms
pthread_mutex(recursive): 498.09 ms
NSRecursiveLock: 576.16 ms
NSConditionLock: 1106.98 ms
@synchronized: 1374.10 ms
转载自:https://juejin.cn/post/7110182189182124068