自旋锁

当线程等待自旋锁时不会进入睡眠,自旋锁由于在获取锁时,线程会一直处于忙等状态,有可能会造成任务的优先级反转。

OSSpinLock

新版 iOS 中,系统维护了 5 个不同的线程优先级/QoS: background,utility,default,user-initiated,user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比它更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级反转问题,从而破坏了 spin lock。

具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 OSSpinLock。

不再安全的 OSSpinLock

互斥锁

当等待互斥锁时,线程会进入睡眠,锁释放时就会唤醒线程。互斥锁又分为递归锁和非递归锁:

pthread_mutex

pthread_mutex 是互斥锁,对性能要求比较高的场景可以使用, API 比较简单:

// 导入头文件
#import <pthread.h>
// 全局声明互斥锁
pthread_mutex_t _lock;
// 初始化互斥锁
pthread_mutex_init(&_lock, NULL);
// 加锁
pthread_mutex_lock(&_lock);
// 这里做需要线程安全操作

// 解锁
pthread_mutex_unlock(&_lock);
// 释放锁
pthread_mutex_destroy(&_lock);

@synchronized

mikeash.com: Friday Q&A 2015-02-20: Let's Build @synchronized

关于 @synchronized,这儿比你想知道的还要多

性能比较低,因为有容错处理,和使用全局表。

  1. 不能使用非OC对象作为加锁条件——id2data中接收参数为id类型
  2. 多次锁同一个对象会有什么后果吗——会从高速缓存中拿到data,所以只会锁一次对象
  3. 都说@synchronized性能低——是因为在底层增删改查消耗了大量性能