这是我参与8月更文挑战的第27天,活动详情查看: 8月更文挑战
锁的分类
我们通常将锁分为自旋锁和互斥锁两大类;
- 互斥锁:
- 互斥锁,有
互斥和同步两个特性;我们的程序在运行过程中,会有多个线程在进行相关的处理,可能会同时处理任务,那么互斥的意思是,当A线程在进行任务操作时,B线程就不能进行操作(闲着);并且需要按照顺序执行; - 互斥锁还分为
递归锁和非递归锁;
- 互斥锁,有
- 自旋锁:
- 同样的两个线程在执行相关的任务,当A线程在处理任务的时候,B线程一直在等待;相当于在
互斥的基础上再加上忙等;(自旋锁也相当于一种特殊的互斥锁)
- 同样的两个线程在执行相关的任务,当A线程在处理任务的时候,B线程一直在等待;相当于在
- 第三种(读写锁)
NSLock和NSRelock分析
NSLock
我们来分析一段代码:
很明显,这段代码存在线程不安全的的情况,运行如下:
想要解决崩溃问题,我们可以使用NSLock进行处理:
我们接下来,再看一段代码演示:
我们可以看到,输入结果没有问题,如果我们在外边加一层for循环呢?
我们看到数据的打印结果已经发生混乱,因为多线程引起的线程不安全导致的;
接下来,我们使用NSLock给计算流程加锁:
我们只要在testMethod函数之前前lock加锁,执行后unlock进行解锁就可以解决问题了;
此处在使用
NSLock的过程中,要防止递归加锁的情况出现;因为NSLock是一把非递归锁;
比如这样:
将会导致递归加锁的情况;
那么,既然NSLock是一把非递归锁,是用的时候要避免递归加锁的情况出现,那么我们直接使用递归锁是不是就能避免这种情况呢?我们用@synchronized这把递归锁尝试一下:
NSRecursiveLock
那么,我们能否使用NSRecursiveLock来解决上述问题呢?
我们修改一下代码,如下:
结果,打印了一遍之后,程序崩溃;这是因为我们的程序是多线程执行的,而NSRecursiveLock并不是一把多线程可递归锁;我们在使用的时候,要注意多线程的问题:
NSRecursiveLock可以解决锁的可递归性,但是却无法解决多线程问题,所以这种情况,我们推荐使用具有多线程可递归性的@synchronized来解决问题:
NSCondition分析
NSCondition的对象实际上作为一个锁和一个线程检查器:锁主要为了当检测条件时保护数据源,执行条件触发的任务;线程检查器主要是根据条件决定是否继续运行线程,即线程是否被阻塞;
相关API的使用:
[condition lock]:一般用于多线程同时访问和修改同一个数据源,保证在同一个时间内数据源只被访问和修改一次,其他线程的命令需要在lock外等待,直到unlock才可继续访问;[condition unlock]:与lock配对使用;[condition wait]:让当前线程处于等待状态;[condition signal]:CPU发信号告诉线程不用再等待,可以继续执行;
我们来模拟一个生产销售模型的情况,代码如下:
我们运行,查看结果:
我们发现,竟然出现了库存为负数的情况;而且当count = 0时,我们在等待,结果下一刻又有了消费;
正确的情况是,生产和消费在保证各自数据安全的情况下,当生产者库存为0时,消费流程应该处于等待状态,并且当有了库存之后,要通知消费方不用再等待:
[_testCondition lock]和[_testCondition unlock]用来保证生产和消费两条线路各自的数据安全;[_testCondition wait]当任务达到某个条件时(生产库存为0),将消费任务暂停,进行等待;[_testCondition signal]当生产有了库存,向消费方发出信号,消费方取消wait等待状态,开始消费;
Foundation源码中锁的封装
NSLock
我们分析NSLock会发现,lock和unlock方法来源于NSLock的NSLocking协议,所以很多自定义的锁都有lock和unlock两个方法,就是因为实现了NSLocking协议;
NSLock是来源于Foundation框架的,但是我们知道OC版本的Foundation框架是没有开源的,那么我们如何研究呢?这里我们可以使用Swift版的Foundation源码进行分析;
我们查找NSLock的实现代码:
我们发现在iOS系统中,NSLock底层是通过pthread_mutex_t来实现的;
我们看一下lock和unlock两个方法的实现:
可以发现,NSLock的lock和unlock两个方法就是通过pthread_mutex_lock(mutex)和pthread_mutex_unlock(mutex)来实现的;
NSRecursiveLock
接下来,我们分析一下NSRecursiveLock的实现:
NSRecursiveLock依然是实现了NSLocking协议,那么它的lock和unlock方法呢?
其和NSLock一样,都是针对pthread_mutex_t的封装,那么就有一个疑问了:既然都是对pthread_mutex_t的封装,那么为什么NSRecursiveLock是可递归锁,而NSLokc确实一把非递归锁呢?
比较两把锁的init方法,我们发现造成一把是递归锁一把是非递归锁的原因是可递归锁NSRecursiveLock的init的实现中设置了pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
NSCondition
分析NSCondition发现,其依然是通过pthread_mutex_t实现的:
lock和unlock方法:
有区别的是NSCondition的deinit方法,比起NSLock和NSRecursiveLock多了cond也就是_ConditionVariablePointer的操作:
以及NSCondition的wait方法的封装:
NSLock,NSRecursiveLock以及NSCondition底层都是对pthread_mutex_t的封装;