文章目录
分类
- 自旋锁
载自旋锁中线程会反复检查变量状态,线程一直保持执行,所以处于忙等待(CPU空转)。一旦获取了自旋锁,线程就会一直保持该锁直到显示释放。自旋锁避免了进程上下文的调度开销,所以适用于线程阻塞很短时间的场合,atomic自带一把自旋锁
OSSpinLock
atomic
- 互斥锁
用于多线程编程中,防止两条线程同时对一公共资源进行读写的机制(将代码切成一个个临界区而达成),获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒
pthread_mutex: C语言层面的互斥锁,较为底层,性能较好
NSLock:OC封装的互斥锁,使用简单
@synchronized:OC语法级别的锁,最方便但是性能较低
objc
#import "LockViewController.h"
#import <pthread.h>
@interface LockViewController ()
@property (nonatomic, assign)int tickets;
@property (nonatomic, strong)NSLock* lock;
@end
@implementation LockViewController {
pthread_mutex_t _mutex;
}
- (void)viewDidLoad {
[super viewDidLoad];
pthread_mutex_init(&_mutex, NULL);
//self.lock = [[NSLock alloc] init];
[self text];
}
- (void)dealloc {
pthread_mutex_destroy(&_mutex);
}
- (void)text {
int tickets = 20;
self.tickets = tickets;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self sellTicket];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 10; i++) {
[self sellTicket];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 10; i++) {
[self sellTicket];
}
});
}
- (void)sellTicket {
//[self.lock lock];
pthread_mutex_lock(&_mutex);
int oldTicket = self.tickets;
[NSThread sleepForTimeInterval:0.2];
oldTicket -= 1;
self.tickets = oldTicket;
pthread_mutex_unlock(&_mutex);
NSLog(@"还剩%ld张票 ---- %@", (long)self.tickets, [NSThread currentThread]);
//[self.lpck unlock];
}
/*
- (void)sellTicket {
@synchronized (self) {
NSInteger oldTicket = self.tickets;
[NSThread sleepForTimeInterval:0.2];
oldTicket--;
self.tickets = oldTicket;
NSLog(@"还剩%ld张票 ---- %@", (long)self.tickets, [NSThread currentThread]);
}
}
*/
@end
显示递归锁:NSRecursiveLock:
objc
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
[lock lock];
// 临界区
[lock unlock];
- 条件锁
即条件变量,当线程的某一些资源要求不满足时就会进入休眠状态,即锁住了,当资源被分配到了,条件锁就打开,线程继续运行
NSCondition
NSConditionLock
objc
@interface TestConditionLock : NSObject
@property (nonatomic, strong) NSConditionLock *conditionLock;
@end
@implementation TestConditionLock
- (instancetype)init {
if (self = [super init]) {
_conditionLock = [[NSConditionLock alloc] initWithCondition:1];
}
return self;
}
- (void)task1 {
[self.conditionLock lockWhenCondition:1];
NSLog(@"执行任务 1");
[self.conditionLock unlockWithCondition:2];
}
- (void)task2 {
[self.conditionLock lockWhenCondition:2];
NSLog(@"执行任务 2");
[self.conditionLock unlock];
}
@end
//调用
TestConditionLock *test = [[TestConditionLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[test task2];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(2);
[test task1];
});//初始状态condition为1,task2卡住,只有当task1执行结束task2才能执行
//_________________
#import "TestCondition.h"
@interface TestCondition()
@property (nonatomic, strong)NSCondition* condition;
@property (nonatomic, assign)bool hasData;
@end
@implementation TestCondition
- (instancetype)init {
if (self = [super init]) {
_condition = [[NSCondition alloc] init];
_hasData = NO;
}
return self;
}
- (void)consumer {
[self.condition lock];
while (!self.hasData) {
NSLog(@"没有数据,线程等待");
[self.condition wait];//当前线程进入等待状态,临时释放condition内部的锁,被唤醒之后再重新尝试加锁。如果wait一致不释放锁,生产者就拿不到锁,也就无法修改条件唤醒消费者
}
NSLog(@"有数据了,开始消费");
[self.condition unlock];
}
- (void)producer {
[self.condition lock];
self.hasData = YES;
NSLog(@"生产了数据,通知消费者");
[self.condition signal];
[self.condition unlock];
}
@end
TestCondition* condition = [[TestCondition alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[condition consumer];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(2);
[condition producer];
});
- 递归锁
同一个线程加锁N次而不会发生死锁,递归锁是一种特殊的互斥锁,即带有递归性质的互斥锁
pthread_mutex(recursive):默认是普通的互斥锁,需要设置相关属性才能变成递归锁
objc
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
//使用:
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
NSRecursiveLock:加几次锁就需要解几次锁
objc
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
[lock lock];
[lock lock];
NSLog(@"不会死锁");
[lock unlock];
[lock unlock];
- 信号量
就是为互斥锁提供了更多的取值空间,可以实现更加复杂的同步。
dispatch_semphore
objc
dispatch_semaphore_create(NSInteger value);//创建信号量,设置初始值
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);//等待信号量,大于0时才能继续
dispatch_semaphore_signal(dispatch_semaphore_t dsema);//发送信号,释放一个信号量
- wait 的时候,如果信号量 > 0,可以继续执行,并且信号量 -1
- 如果信号量 == 0,当前线程等待
- signal 的时候,信号量 +1
- 如果有线程正在等,就唤醒一个线程
- 读写锁
是一种特殊的自旋锁,将对共享资源的访问分成读者和写者,读者只能对资源进行读访问,写者则需要对共享资源进行写操作,相对于自旋锁而言,可以提高并发性。
-
一个读写锁同时只能有一个写者或者多个读者,但不能既有读者又有写者,在读写锁保持期间也是抢占失效的 -
如果
读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里, 直到没有任何写者或读者。如果读写锁没有写者,那么读者可以立即执行读操作
objc
#import <pthread.h>
@interface SafeCache ()
@property (nonatomic, strong) NSMutableDictionary *cache;
@end
@implementation SafeCache {
pthread_rwlock_t _rwlock;
}
- (instancetype)init {
self = [super init];
if (self) {
_cache = [NSMutableDictionary dictionary];
pthread_rwlock_init(&_rwlock, NULL);
}
return self;
}
- (id)objectForKey:(NSString *)key {
pthread_rwlock_rdlock(&_rwlock); // 加读锁
id value = [self.cache objectForKey:key];
pthread_rwlock_unlock(&_rwlock); // 解锁
return value;
}
- (void)setObject:(id)obj forKey:(NSString *)key {
pthread_rwlock_wrlock(&_rwlock); // 加写锁
[self.cache setObject:obj forKey:key];
pthread_rwlock_unlock(&_rwlock); // 解锁
}
- (void)dealloc {
pthread_rwlock_destroy(&_rwlock);
}
@end
详解
自旋锁
OSSpinLock:
不安全,线程获取锁之后会一直处于忙等待状态,可能造成任务的优先级反转,看一个示例如下:
- 低优先级线程 A 拿到了锁
- A 还没执行完,时间片到了,被系统暂停
- 高优先级线程 B 开始执行
- B 想拿锁,但是锁在 A 手里
- B 开始自旋等待
- 因为 B 优先级高,系统一直优先调度 B
- A 优先级低,迟迟得不到 CPU 执行
- A 得不到执行,就无法释放锁
- B 拿不到锁,就继续自旋
在OSSpinLock被弃用之后,替代方案是内部封装os_unfair_lock,这个在加锁时会处于休眠状态。
objc
#import "TestSpinLock.h"
#import <libkern/OSAtomic.h>
@implementation TestSpinLock {
OSSpinLock _lock;
}
- (instancetype)init {
if (self = [super init]) {
_lock = OS_SPINLOCK_INIT;
}
return self;
}
- (void)lowTask {
OSSpinLockLock(&_lock);
NSLog(@"低优先级任务执行开始");
NSLog(@"低优先级任务执行结束");
OSSpinLockUnlock(&_lock);
}
- (void)highTask {
OSSpinLockLock(&_lock);
NSLog(@"高优先级任务执行开始");
NSLog(@"高优先级任务执行结束");
OSSpinLockUnlock(&_lock);
}
@end
TestSpinLock* spin = [[TestSpinLock alloc] init];
NSThread* low = [[NSThread alloc] initWithTarget:spin selector:@selector(lowTask) object:nil];
[low start];
NSThread* high = [[NSThread alloc] initWithTarget:spin selector:@selector(highTask) object:nil];
high.threadPriority = 1.0;
[high start];
//A 持有锁,但没机会运行.B 等待锁,但一直占着 CPU

OS_unfair_lock:
objc
#import "TestSpinLock.h"
#import <os/lock.h>
@implementation TestSpinLock {
os_unfair_lock _lock;
}
- (instancetype)init {
if (self = [super init]) {
_lock = OS_UNFAIR_LOCK_INIT;
}
return self;
}
- (void)lowTask {
os_unfair_lock_lock(&_lock);
NSLog(@"低优先级任务执行开始");
NSLog(@"低优先级任务执行结束");
os_unfair_lock_unlock(&_lock);
}
- (void)highTask {
os_unfair_lock_lock(&_lock);
NSLog(@"高优先级任务执行开始");
NSLog(@"高优先级任务执行结束");
os_unfair_lock_unlock(&_lock);
}
@end

可以发现解决了优先级反转的问题。
原子锁
atomic适用于OC中属性的修饰符,其自带一把自旋锁,但是这个一般基本不使用,都是使用的nonatomic.
首先就是一个setter方法,之前在介绍对象的底层探究有讲到过,是用了一个适配器设计模式,底层都是调用了一个reallySetProperty方法。具体实现这里就不赘述了。
现在的atomic原子锁底层是通过os_unfair_lock替代了OSSpinLock实现的加锁。同时为了防止哈希冲突,还是用了加盐操作。(就是添加了一个变量来区分锁,减少哈希冲突的概率)
互斥锁
synchronized(互斥递归锁)
底层是走objc_sync_enter 和 objc_sync_exit方法实现的。
下面我们详细分析一下这两个方法:
objc_sync_enter
objc
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {//传入不为nil
SyncData* data = id2data(obj, ACQUIRE);//重点
ASSERT(data);
data->mutex.lock();//加锁
} else {//传入nil
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
如果传入的obj存在,则通过id2data方法获取相应的syncData,然后对threadCount和lockcount执行递增操作
如果传入的obj不存在,调用objc_sync_nil,方法直接return
objc_exit_exit
objc
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {//obj不为nil
SyncData* data = id2data(obj, RELEASE);
if (!data) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
} else {
bool okay = data->mutex.tryUnlock();//解锁
if (!okay) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
}
}
} else {//obj为nil时,什么也不做
// @synchronized(nil) does nothing
}
return result;
}
如果obj存在,调用id2Data方法获取对象对应的syncData,然后对threadCount和lockcount执行递减操作
如果obj为nil,啥也不干
那么什么是syncData呢,下面我们了解一下:
首先我们看一下其内部构造:
objc
typedef struct alignas(CacheLineSize) SyncData {
struct SyncData* nextData;//类似链表结构
DisguisedPtr<objc_object> object;
int32_t threadCount; //有多少个线程使用了这个锁
recursive_mutex_t mutex;//锁
} SyncData;
这是一个结构体,有next属性,是一个链表形式的结构,内部封装了recursive_mutex_t属性。存在于由runtime维护的一个全局结构中。
啥是syncCache呢
objc
typedef struct {
SyncData *data;
unsigned int lockCount; // number of times THIS THREAD locked this block
} SyncCacheItem;
typedef struct SyncCache {
unsigned int allocated;
unsigned int used;
SyncCacheItem list[0];//柔性数组
} SyncCache;
同样是一个结构体,主要是用来存储线程的本地缓存数据
id2data分析
objc
static SyncData* id2data(id object, enum usage why)
{
spinlock_t *lockp = &LOCK_FOR_OBJ(object);
SyncData **listp = &LIST_FOR_OBJ(object);
SyncData* result = NULL;
#if SUPPORT_DIRECT_THREAD_KEYS
// 1. 先查线程快速缓存
bool fastCacheOccupied = NO;
SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
if (data) {
fastCacheOccupied = YES;
if (data->object == object) {
uintptr_t lockCount;
result = data;
lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
if (result->threadCount <= 0 || lockCount <= 0) {
_objc_fatal("id2data fastcache is buggy");
}
switch (why) {
case ACQUIRE:
// 当前线程重复加锁,增加重入次数
lockCount++;
tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
break;
case RELEASE:
// 当前线程释放锁,减少重入次数
lockCount--;
tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
if (lockCount == 0) {
// 重入次数为 0,移出快速缓存
tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);
OSAtomicDecrement32Barrier(&result->threadCount);
}
break;
case CHECK:
break;
}
return result;
}
}
#endif
// 2. 再查当前线程的 SyncCache
SyncCache *cache = fetch_cache(NO);
if (cache) {
unsigned int i;
for (i = 0; i < cache->used; i++) {
SyncCacheItem *item = &cache->list[i];
if (item->data->object != object) continue;
result = item->data;
if (result->threadCount <= 0 || item->lockCount <= 0) {
_objc_fatal("id2data cache is buggy");
}
switch (why) {
case ACQUIRE:
// 同一线程再次进入,增加重入次数
item->lockCount++;
break;
case RELEASE:
// 退出同步块,减少重入次数
item->lockCount--;
if (item->lockCount == 0) {
// 当前线程完全释放该锁,从缓存中移除
cache->list[i] = cache->list[--cache->used];
OSAtomicDecrement32Barrier(&result->threadCount);
}
break;
case CHECK:
break;
}
return result;
}
}
// 3. 线程缓存没找到,进入全局 SyncData 链表查找
lockp->lock();
{
SyncData* p;
SyncData* firstUnused = NULL;
for (p = *listp; p != NULL; p = p->nextData) {
if (p->object == object) {
// 找到对象对应的 SyncData
result = p;
OSAtomicIncrement32Barrier(&result->threadCount);
goto done;
}
if ((firstUnused == NULL) && (p->threadCount == 0)) {
firstUnused = p;
}
}
// RELEASE 或 CHECK 找不到时,直接结束
if ((why == RELEASE) || (why == CHECK)) {
goto done;
}
// 复用空闲的 SyncData
if (firstUnused != NULL) {
result = firstUnused;
result->object = (objc_object *)object;
result->threadCount = 1;
goto done;
}
}
// 4. 没有可复用的 SyncData,则新建一个
posix_memalign((void **)&result, alignof(SyncData), sizeof(SyncData));
result->object = (objc_object *)object;
result->threadCount = 1;
new (&result->mutex) recursive_mutex_t(fork_unsafe_lock);
result->nextData = *listp;
*listp = result;
done:
lockp->unlock();
if (result) {
if (why == RELEASE) {
return nil;
}
if (why != ACQUIRE) {
_objc_fatal("id2data is buggy");
}
if (result->object != object) {
_objc_fatal("id2data is buggy");
}
#if SUPPORT_DIRECT_THREAD_KEYS
if (!fastCacheOccupied) {
// 保存到线程快速缓存
tls_set_direct(SYNC_DATA_DIRECT_KEY, result);
tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)1);
} else
#endif
{
// 保存到当前线程的 SyncCache
if (!cache) cache = fetch_cache(YES);
cache->list[cache->used].data = result;
cache->list[cache->used].lockCount = 1;
cache->used++;
}
}
return result;
}
| 对比项 | TLS | SyncCache |
|---|---|---|
| 本质 | 线程本地快速缓存 | 线程本地普通缓存 |
| 存储数量 | 只存一组 SyncData + lockCount |
可存多组 SyncData + lockCount |
| 查找方式 | 直接通过 key 取 | 遍历 cache->list |
| 速度 | 最快 | 较快 |
| 适合场景 | 同一线程反复锁同一个对象 | 同一线程持有多个对象锁 |
| lockCount 位置 | TLS 中单独保存 | SyncCacheItem.lockCount |
| 是否线程私有 | 是 | 是 |
| 是否访问全局表 | 不访问 | 不访问 |
| 源码位置 | tls_get_direct(...) |
fetch_cache(...) |
| 作用 | 快速判断当前线程是否已经锁过该对象 | 记录当前线程已经持有的多个锁 |
我们使用自然语言概述一下流程:
- 首先在tls(线程局部存储)中查询(tls存储线程最近一次使用的锁对象以及嵌套锁计数)
- 通过tls_get_direct获取线程绑定的syncData,检查data->object是否与传入的对象匹配。如果匹配执行相应的加锁或者解锁操作,若归零则移除 TLS 缓存并原子递减
threadCount。如果出现threadCount <= 0或者lockCount <= 0,触发崩溃。 - 反之进入下一步查询
- 通过tls_get_direct获取线程绑定的syncData,检查data->object是否与传入的对象匹配。如果匹配执行相应的加锁或者解锁操作,若归零则移除 TLS 缓存并原子递减
- 查询syncCache线程存储(线程存储已经持有的多个锁对象,支持多锁的交替使用)
- 通过
fetch_cache(NO)获取线程缓存池,遍历SyncCacheItem数组,检查item->data->object是否匹配,然后执行对应的加锁解锁操作,若归零则从数组中移除并原子递减threadCount。同样如果出现threadCount <= 0或者lockCount <= 0,触发崩溃。 - 反之进入下一步查询
- 通过
- 查询全局链表与创建syncData节点(找到活着创建之后还要放入当前线程的缓存中去,tls和syncCache二选一)
- 如果全局链表里找到了 object 对应的 SyncData,说明这个
object以前已经被创建过锁数据了。可能是其他线程正在用,也可能是之前用过还没完全释放。这时不会重新创建SyncData,而是直接复用已有的节点。 - 如果全局链表中没有找到,但是有空闲的syncData节点,可以进行复用。
- 如果局链表里完全没有可用的 SyncData,那就真正创建一个新的
SyncData。
- 如果全局链表里找到了 object 对应的 SyncData,说明这个
| 场景 | 操作 |
|---|---|
| TLS 找到 | 当前线程重入,lockCount++ |
| SyncCache 找到 | 当前线程重入,lockCount++ |
| 全局链表找到 | 复用已有 SyncData,threadCount++ |
| 全局链表没找到,但有空闲 SyncData | 复用空闲节点,threadCount = 1 |
| 全局链表完全没有 | 创建新的 SyncData,threadCount = 1 |
@synchronized坑点
我们看一下下面这段代码:
objc
- (void)cjl_testSync{
_testArray = [NSMutableArray array];
for (int i = 0; i < 200000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@synchronized (self.testArray) {
self.testArray = [NSMutableArray array];
}
});
}
}
运行这段代码可以发现报错了,这是为啥嘞?
- 首先就是因为我们锁对象在循环中一直被修改释放,不同线程可能使用的是不同的锁对象,无法实现真正的互斥
- 其次就是@synchronized底层通过全局哈希表管理锁数据,如果锁对象频繁的变化,会触发大量的syncData节点的创建与销毁,导致内存管理异常或者链表断裂。
所以我们在选择锁对象的时候,使用一个独立且稳定的锁对象作为锁,可以有效的避免锁对象动态变化带来的负面效果。
同时我们也可以控制线程的并发量:改用串行队列或者限制并发数量(NSOperationQueue.maxConcurrentOperationCount),避免资源耗尽
NSLock
对于嵌套次数过多的情况,我们可以使用NSLock避免@synchronized带来的性能消耗问题。底层是对pthread_mutex的封装,是一个非递归互斥锁,如果发生递归调用,线程会死锁(表现为阻塞)
在同一线程上调用NSLock的两次lock方法将永久锁定线程
pthread_mutex
是NSLock的子类,底层也是对pthread_mutex的封装。当锁被占用时,其他线程申请锁时,不会一直忙于等待,而是阻塞线程并睡眠。具体使用方法前面有涉及过。
条件锁
NSCondition
objc
NSCondition *condition = [[NSCondition alloc] init]
[condition lock];
[condition unlock];
//让当前线程处于等待状态
[condition wait];
//CPU发信号告诉线程不用在等待,可以继续执行
[condition signal];
NSCondition 是一个条件锁,在日常开发中使用较少,与信号量有点相似:线程1需要满足条件1才会往下走,否则会堵塞等待,知道条件满足。经典模型是生产消费者模型
NSCondition的对象实际上作为一个锁和一个线程检查器:
主要是为了当检测条件时保护数据源,执行条件引发的任务。根据条件决定是否继续运行线程,即线程是否被阻塞。
我们看一下其Swift源码:
objc
open class NSCondition: NSObject, NSLocking {
internal var mutex = _MutexPointer.allocate(capacity: 1)
internal var cond = _ConditionVariablePointer.allocate(capacity: 1)
//初始化
public override init() {
pthread_mutex_init(mutex, nil)
pthread_cond_init(cond, nil)
}
//析构
deinit {
pthread_mutex_destroy(mutex)
pthread_cond_destroy(cond)
mutex.deinitialize(count: 1)
cond.deinitialize(count: 1)
mutex.deallocate()
cond.deallocate()
}
//加锁
open func lock() {
pthread_mutex_lock(mutex)
}
//解锁
open func unlock() {
pthread_mutex_unlock(mutex)
}
//等待
open func wait() {
pthread_cond_wait(cond, mutex)
}
//等待
open func wait(until limit: Date) -> Bool {
guard var timeout = timeSpecFrom(date: limit) else {
return false
}
return pthread_cond_timedwait(cond, mutex, &timeout) == 0
}
//信号,表示等待的可以执行了
open func signal() {
pthread_cond_signal(cond)
}
//广播
open func broadcast() {
// 汇编分析 - 猜 (多看多玩)
pthread_cond_broadcast(cond) // wait signal
}
open var name: String?
}
可以发现,NSCondition是对pthread_mutex的封装,其中:
- cond就是用于访问和操作特定类型数据的指针
wait操作会阻塞线程,使其进入休眠状态,直至超时signal操作是唤醒一个正在休眠等待的线程broadcast会唤醒所有正在等待的线程
具体的使用示例前面有写过。
NSConditionLock
条件锁,一旦一个线程获得锁,其他线程一定等待。
objc
// 初始化条件锁,设置内部 condition 初始值为 2
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
// 普通加锁:
// 只判断这把锁有没有被其他线程占用
// 不判断内部 condition 的值
[conditionLock lock];
// 条件加锁:
// 只有同时满足下面两个条件,才能继续执行:
// 1. 当前锁没有被其他线程占用
// 2. 内部 condition == A条件
[conditionLock lockWhenCondition:A条件];
// 普通解锁:
// 只释放锁
// 不修改内部 condition 的值
[conditionLock unlock];
// 条件解锁:
// 释放锁
// 同时把内部 condition 修改为 A条件
[conditionLock unlockWithCondition:A条件];
// 带超时的条件加锁:
// 在 A时间 之前等待 condition == A条件
// 如果条件满足并成功拿到锁,返回 YES
// 如果超时还没拿到锁,返回 NO
BOOL result = [conditionLock lockWhenCondition:A条件 beforeDate:A时间];
if (result) {
// 成功拿到锁,可以执行临界区代码
[conditionLock unlock];
} else {
// 没有拿到锁,不能调用 unlock
}
使用场景
如果只是简单的使用,比如设计一个线程安全问题,使用NSLock即可
如果是循环嵌套,建议使用递归锁,性能好
如果在循环嵌套中,还有多线程影响时,(如等待、死锁问题)建议使用@synchronized