多线程确实可以大大提高我们的app的运行效果和体验

但是有时候也容易引发一些问题

比如资源的抢占问题 多个线程同时访问同一个对象 同一个文件或者变量

60ee6cc86ba12.png

先简单介绍一下自旋锁和互斥锁

自旋锁是高级锁 不会进入休眠 会一直在那里等待 处于忙等状态

互斥锁在等待的时候会进入休眠 然后等待唤醒

不过进入睡眠和醒来都是会消耗性能的

什么情况使用自旋锁比较划算?

预计等待锁的时间比较短

加锁的代码区块经常被调用,但竞争情况很少发生

CPU资源不紧张

多核处理器

什么情况使用互斥锁比较划算?

预计线程等待时间比较长

单核处理器

临界区有IO操作

临界区代码复杂或者循环量大

临界区竞争非常激烈

@synchronized

创建单例的时候使用

OSSpinLock

OSSpinLock是自旋锁

首先这个锁的效率是很高的 基本在所有的锁里面最高的 但是因为这个锁现在已经不安全了 原因如下

如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock

简而言之 就是OSSpinLock有潜在的优先级反转问题

以上就是出现优先级反转的场景 需要注意,那么怎么预防呢?

当优先级低的任务进来的时候 这时候要是有高优先级的任务等待中 那么只需要提升这个优先级低的任务 让他高于这个高优先级的 等执行完之后 再恢复到原来的优先级,当然这个优先级高的线程为什么一直在这等着 这需要注意 是否必要,总之要使临界区的锁时间尽可能的短

os_unfair_lock

从ios10开始 苹果官方推荐使用 os_unfair_lock

这个锁的效率也是极高的

官方文档里面有对os_unfair_lock的描述是low level lock 它会在等待的时候进入休眠状态 而互斥锁的特点就是在等待的时候会进入休眠 所以我们也可以把os_unfair_lock看做是互斥锁 用来代替NSSpinLock的

pthread_mutex

pthread_mutex 是c底层的线程锁 (是一个跨平台的锁)

pthread_mutex 会初始化一个条件 决定它是自旋锁还是互斥锁

因为是C语言函数 所以系统不会自动销毁 用完之后需要手动销毁

NS系列锁

NS系列锁指的是NSLock、NSCondition、NSConditionLock、NSRecursiveLock,之所以把这几个放在一起,是因为它们都遵守NSLocking协议,就俩方法,加锁解锁,so easy!

@protocol NSLocking

- (void)lock;

- (void)unlock;

@end

这里补充一下上锁的两种方式trylock和lock使用场景:

当前线程锁失败,也可以继续其它任务,用 trylock 合适

当前线程只有锁成功后,才会做一些有意义的工作,那就 lock,没必要轮询 trylock

lockBeforeDate:这个方法表示会在传入的时间内尝试加锁,若能加锁则执行加锁操作并返回 YES,反之返回 NO。

NSLock线程锁

NSCondition 条件锁

NSRecursiveLock是对 递归锁的封装

NSConditionLock 是对pthread_mutex 和conditon条件的封装

@synchronized 性能是最差的

知识小结

  1. 所有的锁基本都是创建锁、加锁、等待、解锁的流程,所以并不复杂。

  2. 如果追求锁的极致性能,可以考虑更偏底层实现的pthread_mutex互斥锁以及信号量的方式。

  3. @synchronized的效率最低,但是它使用最方便,所以如果没有性能瓶颈的话使用它也不错。