NSLock
使用NSLock
将加锁解锁操作放在正确的位置
image.png
这里
NSlock
起到了作用,接下来将lock
操作放到testMethod
内部,见下图:image.png
这个时候,并没有正场的打印。是因为
textMethod
后面进行lock加锁,内部又继续调用textMethod
,导致重复加锁
对于NSLock锁。被它锁住的区域(代码块)。没解锁之前,不能被其它线程访问。所以外面的for循环 异步并行队列被阻塞了。只能依次执行。但是当将lock
操作放入内部,textMethod
方法还是处于循环调用状态,这就导致了重复加锁
【结论】NSLock不支持重复加锁
NSRecursiveLock
使用NSRecursiveLock
将加锁解锁操作放在正确的位置
image.png
这里
NSRecursiveLock
起到了作用,接下来将lock
操作放到testMethod
内部,见下图:image.png
这里发现只运行了一次就发生了崩溃
【结论】NSRecursiveLock只支持单线程单线程内递归加锁,不支持多线程
NSCondition
NSCondition
的对象实际上作为一个锁和一个线程检查器:锁主要为了当检测条件时保护数据源,执行条件引发的任务;线程检查器主要是根据条件决定是否执行线程,即线程是否被阻塞
- [condition lock] 一般用于多线程同时访问、修改同一个数据源,保证在同一时间内数据源只能被访问、修改一次,其他线程的命令需要在
lock
外等待,直到unlock
才能被访问 - [condition unlock]:与
lock
同时使用 - [condition wait] 让当前线程处于等待状态
- [condition signal] CPU发出信号告诉线程不用在等待,可以继续执行
实例
NSCondition
常用与生产销售模型,这里开启多个线程进行产品生产和销售
image.png
这里的生产和消费进行加锁处理,来保证多线程的安全。与此同时,生产与消费之间也存在 库存数目的关系,当库存数目不足时需要
[_testCondition wait];
进行等待,让消费窗口停止消费,[_testCondition signal]
表示库存充足,可以进行消费image.png
NSLocationLock
NSLocationLock
也是一种条件锁,一旦⼀个线程获得锁,其他线程⼀定等待
- [xxxx lock]; 表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition) 那它能执⾏此⾏以下代码,如果已经有其他线程获得锁(可能是条件锁,或者⽆条件锁),则等待,直⾄其他线程解锁
- [xxx lockWhenCondition:A条件]; 表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进⼊代码区,同时置它获得该锁,其他任何线程都将等待它代码的完成,直⾄它解锁
- [xxx unlockWithCondition:A条件]; 表示释放锁,同时把内部的condition设置为A条件
- return = [xxx lockWhenCondition:A条件 beforeDate:A时间]; 表示如果被锁定(没获得锁),并超过该时间则不再阻塞线程。但是注意:返回的值是NO,它没有改变锁的状态,这个函数的⽬的在于可以实现两种状态下的处理
来看下下面的案例
image.png
image.png
这里的打印是321
,3
在2
前面我们可以理解,毕竟2
处有个sleep
,至于1
为什么在后面呢?
是因为在初始化的时候condition
传的是2
,所以先执行2
,至于3
它是不受condition
的影响。只有执行2
的任务,将condition
变为1
才会执行1
Foundation库锁源码了解
image.png
image.png
我们看到
NSLock,NSRecursiveLock,NSCondition
都是遵守的NSLocking
协议,但是这部分源码是在Foundation
框架下,OC
并没有开源,但是swift的Foundation
框架是开源的,打开Foundation
源码
NSLock
全局搜索NSLock
,在初始化中,调用了pthread_mutex_init
image.png
在
lock
中调用了pthread_mutex_lock
,unlock
中调用了pthread_mutex_unlock
image.png
NSRecursiveLock
image.png
NSRecursiveLock
与NSLock
逻辑基本相同,唯一不同的是增加了pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
这行代码,也就是增加了锁的递归性
自定义读写锁(多读单写)
多读单写
要求
- 多读不互斥
- 读写互斥
- 写写互斥
- 不能阻塞主线程
@interface ViewController ()
@property (nonatomic,assign)NSInteger ticketCount;
// 并发队列
@property (nonatomic,strong)dispatch_queue_t safeQueue;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.ticketCount = 0;
self.safeQueue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i ++) {
[self writeticket];
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
for (int i = 0; i < 10; i ++) {
[self readTicket];
}
}
- (void)readTicket{
dispatch_async(dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT), ^{
dispatch_sync(self.safeQueue, ^{
NSLog(@"读取:%ld",self.ticketCount);
});
});
}
- (void)writeticket{
dispatch_barrier_async(self.safeQueue, ^{
sleep(2);
self.ticketCount ++;
NSLog(@"写入:%ld",self.ticketCount);
});
}
- 写入使用
dispatch_barrier_async
,由于队列是相同的,所以实现了写写互斥
- 读取首先在外层使用
dispatch_async
,是为了防止主线程阻塞。内部使用dispatch_sync
且队列与写入的队列相同,是为了实现多读不互斥,并且读写互斥