- NSLock
- NSLock是Cocoa提供给我们最基本的锁对象,这也是我们经常使用的,除lock和unlock外,NSLock还提供了tryLock和lockBeforeDate:两个方法,前一个方法会尝试加锁,如果锁不可用(已经被锁住),并不会阻塞线程,直接返回NO。后一个方法则会在指定的Date之前尝试加锁,如果在指定的时间内都不能加锁,则返回NO
- synchronized(互斥锁)
- synchronized会创建一个异常捕获handler和一些内部的锁,所以使用@synchronized替换普通锁的代价是要付出更多的时间消耗
- 创建给给@synchronized指令的对象是一个用来区别保护块的唯一标识符。如果你在两个不同的线程里面执行上述方法,每次在一个线程传递了一个不同的对象给anObj参数,那么每次都将会拥有它的锁,并持续处理,中间不会被其他线程阻塞。然而如果你传递的是同一个对象,那么多个线程中的一个线程会首先获得该锁,而其他线程将会被阻塞直到第一个线程完成它的临界区
- 作为一个预防措施。@synchronized块隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁,这就意味着为了使用@synchronized指令,你必须在你的代码中启用异常处理。如果你不想让隐式的异常处理例程带来额外的开销,那么可以使用其他的锁
- atomic
- atomic只是给成员变量的set和get方法加了一个锁,防止多线程一直去读写这个成员变量。但这也仅仅是对读写的锁定,并不是线程安全。而且使用atomic比nonatomic慢了将近20倍
- OSSpinlock
- 自旋锁
- 耗时最少
- 自旋锁几乎不进入内核,仅仅是重新加载自旋锁
- 如果自旋锁被占用时间在一百纳秒以内,性能还是比较高的,因为减少了代价较高的系统调用和一系列的上下文切换
- 但是该锁不是万能的,如果该锁占用的时间比较多的时候,使用该锁会导致占用的cpu较多
- pthread_mutex
- 是底层的API,在各种加锁方式中属于性能比较高的
- 如果自旋锁占用的时间比较多,那么使用pthread是一个不错的选择
- NSConditionLock(条件锁)
- 条件锁与特定的与用户定义的条件有关,它可以确保一个线程可以获取满足一定条件的锁
- 内部涉及到信号量机制,一旦一个线程获取锁以后,它可以放弃锁并设置相关条件,这时候其他锁竞争该锁
- 线程之间的竞争激烈,涉及到条件锁检测、线程间通信、系统调用,上下文切换比较频繁
- NSRecursiveLock递归锁
- NSRecursiveLock实际上定义的是一个递归锁,这个锁可以被同一线程多次请求,而不会引起死锁。这主要是用在循环或者递归操作中
- 总结:
- 如果只是粗略的使用锁,不考虑性能可以使用synchronized
- 如果对效率有较高的要求,采用OSSpinLock
- 因为pthread的锁也是使用OSSpinLock实现的,而且在OSSpinLock的实现过程中,并没有进入系统kernel,使用OSSpinLock可以节省系统调用和上下文切换
- NSLock/NSConditionLock/NSRecursive耗时接近。220ms左右
- dispatch_barrier_async的性能并没有我们想象中的纳闷好,这与线程同步调度开销有关