主要讲解NSLock/NSCondition/NSRecursiveLock/锁的基本用法
常见锁的分类:
- 自旋锁
OSSpinLock
- 互斥锁
os_unfair_lock
- 互斥/递归/条件锁
pthread_mutex_t
- 互斥锁
NSLock
- 递归锁
NSRecursiveLock
- 条件锁
NSCondition
- 条件锁
NSConditionLock
- 递归锁
@synchronized
- 信号量
semaphore
- 读写锁
pthread_rwlock_t
- 异步栅栏
dispatch_barrier_async
iOS 锁 部分一
iOS 锁 部分二
iOS 锁 部分三
iOS 锁 部分四
1. 互斥锁NSLock
特点:
- 基于
mutex
基本锁的封装,更加面向对象, 等待锁的线程会处于休眠状态; - 遵守
NSLocking
协议; NSLocking协议中两个方法如下:- (void)lock; - (void)unlock;
- 可能会用到的方法
3.1 初始化跟其他OC
对象一样, 直接进行alloc
和init
操作;
3.2- (void)lock
加锁;
3.3- (void)unlock
解锁;
3.4- (BOOL)tryLock
尝试加锁;
3.5- (BOOL)lockBeforeDate:(NSDate *)limit
在某一个时间点之前等待加锁; - 测试代码:
#import "ViewController5.h"
@interface ViewController5 ()
@property (nonatomic, strong) NSLock *lock;
@end
@implementation ViewController5
- (void)viewDidLoad {
[super viewDidLoad];
self.lock = [[NSLock alloc] init];
[self handleMoney];
}
- (void)handleMoney {
self.money = 100;
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf saveMoney];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf drawMoney];
}
});
}
- (void)saveMoney{
[self.lock lock];
[super saveMoney];
[self.lock unlock];
}
- (void)drawMoney {
[self.lock lock];
[super drawMoney ];
[self.lock unlock];
}
@end
2. 递归锁 NSRecuresiveLock
特点
- 基于
mutex
递归锁的封装,所以这是一个递归锁; - 遵守
NSLocking
协议; NSLocking协议中两个方法如下:- (void)lock; - (void)unlock;
- 可能会用到的方法
3.1 初始化跟其他OC
对象一样, 直接进行alloc
和init
操作;
3.2- (void)lock
加锁;
3.3- (void)unlock
解锁;
3.4- (BOOL)tryLock
尝试加锁;
3.5- (BOOL)lockBeforeDate:(NSDate *)limit
在某一个时间点之前等待加锁; - 测试代码
#import "ViewController7.h"
@interface ViewController7 ()
@property (nonatomic, strong) NSRecursiveLock *lock;
@end
@implementation ViewController7
- (void)viewDidLoad {
[super viewDidLoad];
self.lock = [[NSRecursiveLock alloc] init];
}
/*************************************递归锁********************************************/
- (void)recuresiveAction {
static int count = 10;
[self.lock lock];
NSLog(@"count: %d", count);
if (count > 0) {
count --;
[self recuresiveAction];
}
[self.lock unlock];
}
/*************************************递归锁********************************************/
@end
3. 条件锁 NSCondition
特点
- 基于
mutex
基础锁和cont
条件的封装, 所以它是互斥锁而且自带条件, 等待锁的线程休眠; - 遵守
NSLocking
协议; NSLocking协议中两个方法如下:- (void)lock; - (void)unlock;
- 可能会用到的方法
3.1 初始化跟其他OC
对象一样, 直接进行alloc
和init
操作;
3.2- (void)lock
加锁;
3.3- (void)unlock
解锁;
3.4- (BOOL)tryLock
尝试加锁;
3.5- (BOOL)lockBeforeDate:(NSDate *)limit
在某一个时间点之前等待加锁;
3.6- (void)wait
等待条件(进入休眠的同时放开锁, 被唤醒的同时再次加锁)
3.7- (void)signal
发送信号激活等待该条件的线程;
3.8- (void)broadcast
发送广播信号激活等待该条件的所有线程; - 测试代码
#import "ViewController6.h"
@interface ViewController6 ()
@property (nonatomic, strong) NSMutableArray *dataArr;
@property (nonatomic, strong) NSCondition *condition;
@end
@implementation ViewController6
- (void)viewDidLoad {
[super viewDidLoad];
/**
首先设定以下使用场景, 两条线程 A和B, A线程中执行删除数组元素, B 线程中执行添加数组元素;
由于不知道哪个线程会先执行, 所以需要加锁实现, 只有在添加后才能执行删除操作;为互斥锁添加条件可以实现;
*/
self.condition = [[NSCondition alloc] init];
NSThread *deThread = [[NSThread alloc] initWithTarget:self selector:@selector(deleteObj) object:nil];
[deThread start];
///sleep一秒. 确保删除元素的线程先获得锁;
sleep(1);
NSThread *adThread = [[NSThread alloc] initWithTarget:self selector:@selector(addObj) object:nil];
[adThread start];
}
- (void)deleteObj {
[self.condition lock];
NSLog(@"delete begin");
///添加判断, 如果没有数据则添加条件
if (self.dataArr.count < 1) {
/**
添加条件, 如果数组为空, 则添加等待线程休眠, 将锁让出; 接受到信号时会再次加锁, 然后继续向下执行;
*/
[self.condition wait];
}
[self.dataArr removeLastObject];
NSLog(@"数组执行删除元素操作");
[self.condition unlock];
}
- (void)addObj {
[self.condition lock];
NSLog(@"add begin");
[self.dataArr addObject:@"HTI"];
///发送新号, 说明已经添加元素了;
[self.condition signal];
// ///通知所有符合条件的线程
// [self.condition broadcast];
NSLog(@"数组执行添加元素操作");
[self.condition unlock];
}
- (NSMutableArray *)dataArr {
if (!_dataArr) {
_dataArr = [NSMutableArray arrayWithCapacity:0];
}
return _dataArr;
}
@end
4. 条件锁 NSConditionLock
特点
- 基于
NSCondition
的进一步封装, 可以更加高级的设置条件值;
假设有这样一个场景, 三个线程
A B C
,执行完A
线程后才能执行B
, 执行完B
线程后执行C
; 就是为线程之间的执行添加依赖;NSConditionLock
可以方便的实现这个功能;
- 遵守
NSLocking
协议; NSLocking协议中两个方法如下:- (void)lock; - (void)unlock;
- 可能会用到的方法
3.1 初始化跟其他OC
对象一样, 直接进行alloc
和initWithCondition:(NSInteger)condition
操作; (如果使用init
方法, 则condition
默认为0
);
3.2 有一个属性是@property (readonly) NSInteger condition
用来设定条件值, 如果不设定, 则默认为零;
3.3- (void)lock
直接加锁;
3.4- (void)lockWhenCondition:(NSInteger)condition
根据condition
值加锁, 如果值不满足, 则不加;
3.5- (void)unlock
解锁;
3.6- (void)unlockWithCondition:(NSInteger)condition
解锁, 并设定condition
的值;
3.7- (BOOL)tryLock
尝试加锁;
3.8- (BOOL)lockBeforeDate:(NSDate *)limit
在某一个时间点之前等待加锁; - 测试代码
#import "ViewController8.h"
@interface ViewController8 ()
@property (nonatomic, strong) NSConditionLock *lock;
@end
@implementation ViewController8
- (void)viewDidLoad {
[super viewDidLoad];
// 如果不设置 condition 的值则默认为0
// self.lock = [[NSConditionLock alloc] init];
self.lock = [[NSConditionLock alloc] initWithCondition:0];
[self createThreads];
}
- (void)createThreads {
/*
需要执行的顺序为 A-B-C, 但是因为在子线程中所以我们不能确定谁先执行, 添加 sleep 使问题更突出点;
*/
NSThread *c = [[NSThread alloc] initWithTarget:self selector:@selector(threadC) object:nil];
[c start];
sleep(0.2);
NSThread *b = [[NSThread alloc] initWithTarget:self selector:@selector(threadB) object:nil];
[b start];
sleep(0.2);
NSThread *a = [[NSThread alloc] initWithTarget:self selector:@selector(threadA) object:nil];
[a start];
}
- (void)threadA {
[self.lock lockWhenCondition:0];
NSLog(@"A ThreadExcute");
[self.lock unlockWithCondition:1];
}
- (void)threadB {
[self.lock lockWhenCondition:1];
NSLog(@"B ThreadExcute");
[self.lock unlockWithCondition:2];
}
- (void)threadC {
[self.lock lockWhenCondition:2];
NSLog(@"C ThreadExcute");
[self.lock unlock];
}
@end