@synchronized
NSLock
、pthread_mutex
NSCondition
NSConditionLock
NSRecursiveLock
pthread_mutex(recursive)
dispatch_semaphore
在探索源码以前先写一个票的demo,先看没有加锁的状况下运行是怎样的swift
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.ticketCount = 20;
[self lg_testSaleTicket];
}
- (void)lg_testSaleTicket{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 3; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 10; i++) {
[self saleTicket];
}
});
}
- (void)saleTicket{
if (self.ticketCount > 0) {
self.ticketCount--;
sleep(0.1);
NSLog(@"当前余票还剩:%ld张",self.ticketCount);
}else{
NSLog(@"当前车票已售罄");
}
}
复制代码
运行结果: 缓存
@synchronized
从上面的实例代码中能够看到多线程访问同一个数据的时候会出现问题,可能同时一个多个线程访问一个数据,此时为了不这种问题能够加锁同时只让一个线程访问数据,具体用法以下:再看运行结果:
发现测试就没有上述问题了。
再看源码实现 首先开启汇编调试 发现底层会调用两个方法:
objc_sync_exit
和objc_sync_enter
,也能够经过clang查看编译后的文件验证此时在下个符号断点
objc_sync_exit
和objc_sync_enter
发现源码在
libobjc.A.dylib
库中,而后再去库里面找源码
先看SyncData
结构安全
typedef struct alignas(CacheLineSize) SyncData {
struct SyncData* nextData;
DisguisedPtr<objc_object> object;
int32_t threadCount; // number of THREADS using this block
recursive_mutex_t mutex;
} SyncData;
复制代码
发现是一个单链表结构markdown
nextData
指向下一个SyncDataobject
一个对象指针,对象是objc_object即OC对象,不难猜想,它保存了被锁定对象obj的指针threadCount
记录正在使用这个代码块的线程数mutex
递归锁、获取到该结构体对象后,就是调用它的lock()方法再看id2data
源码 具体源码注释图中都有解释。
总结一下大体流程:数据结构
lockCount+1
返回就好,objc_sync_exit
方法对应的是减一lockCount+1
返回,objc_sync_exit
方法对应的是减一threadCount
加1而且存到缓存中,若是也没有其余线程使用则threadCount
置为1存到缓存中缓存结构图: 多线程
OSSpinLock
OSSpinLock
被弃用,其替代方案是内部封装了os_unfair_lock
,而os_unfair_lock
在加锁时会处于休眠状态,而不是自旋锁的忙等状态并发
atomic适用于OC中属性的修饰符,其自带一把自旋锁,属性在调用setter
和getter
方法的时候会加一把锁框架
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
...
id *slot = (id*) ((char*)self + offset);
...
if (!atomic) {//未加锁
oldValue = *slot;
*slot = newValue;
} else {//加锁
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
...
}
复制代码
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();//加锁
id value = objc_retain(*slot);
slotlock.unlock();//解锁
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
复制代码
从源码中能够看出,对于atomic修饰的属性,进行了spinlock_t加锁处理,可是在前文中提到OSSpinLock已经废弃了,这里的spinlock_t在底层是经过os_unfair_lock替代了OSSpinLock实现的加锁async
pthread_mutex
pthread_mutex
就是互斥锁,当锁被占用,其余线程申请锁时,不会一直忙等待,而是阻塞线程并睡眠
使用示例:函数
// 导入头文件
#import <pthread.h>
// 全局声明互斥锁
pthread_mutex_t _lock;
// 初始化互斥锁
pthread_mutex_init(&_lock, NULL);
// 加锁
pthread_mutex_lock(&_lock);
// 这里作须要线程安全操做
// 解锁
pthread_mutex_unlock(&_lock);
// 释放锁
pthread_mutex_destroy(&_lock);
复制代码
NSLock
首先通关断点调试查看NSLock
源码的位置以下图: 此时发现
NSLock
的源码在Foundation
框架中,由于OC
的Foundation
框架是闭源的因此看不了源码,可是swift
的Foundation
框架是开源的,因此咱们也已查看swift
的Foundation
框架,由于也就是语法不同大致实现逻辑都差很少 能够发现
NSLock
底层就是对pthread_mutex
的封装,应为NSLock
是一把互斥锁,会阻塞线程等待任务执行,因此使用NSLock
须要注意不能重入NSLock
锁,会形成线程相互等待的状况,形成死锁
NSRecursiveLock
是互斥锁中的递归锁,可被同一线程屡次获取的锁,而不会产生死锁。什么意思呢,一个线程已经得到了锁,开始执行受锁保护的代码(锁还未释放),若是这段代码调用了其余函数,而被调用的函数又要获取这个锁,此时已然能够得到锁并正常执行,而不会死锁。底层也是对pthread_mutex
的封装底层实现代码也和NSLock
很想lock
方法和unLock
方法都和NSLock
是同样的无非就是init
的时候NSRecursiveLock
设置了该锁的类型是个递归锁 使用示例:
NSCondition
NSCondition
也是一把互斥锁他和NSLock
的区别在于
NSLock
在获取不到锁的时候自动使线程进入休眠,锁被释放后线程又自动被唤醒
NSCondition
可使咱们更加灵活的控制线程状态,在任何须要的时候使线程进入休眠或唤醒它
例如一个生产消费的例子,只有生产出来了商品才能被消费者售卖,消费者再买东西的时候商品没了就要等待生产者产出后在进行购买,示例代码以下:
- (void)td_testConditon{
_testCondition = [[NSCondition alloc] init];
//建立生产-消费者
for (int i = 0; i < 50; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self td_producer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self td_consumer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self td_consumer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self td_producer];
});
}
}
- (void)td_producer{
[_testCondition lock]; // 操做的多线程影响
self.ticketCount = self.ticketCount + 1;
NSLog(@"生产一个 现有 count %zd",self.ticketCount);
[_testCondition signal]; // 信号
[_testCondition unlock];
}
- (void)td_consumer{
[_testCondition lock]; // 操做的多线程影响
if (self.ticketCount == 0) {
NSLog(@"等待 count %zd",self.ticketCount);
[_testCondition wait];
}
//注意消费行为,要在等待条件判断以后
self.ticketCount -= 1;
NSLog(@"消费一个 还剩 count %zd ",self.ticketCount);
[_testCondition unlock];
}
复制代码
底层和
NSLock
很像都是对pthread_mutex_t
的封装,无非就是使用了pthread_cond_t
的条件
NSConditionLock
条件锁,通俗的将就是有条件的互斥锁
使用NSConditionLock对象,能够确保线程仅在知足特定条件时才能获取锁。 一旦得到了锁并执行了代码的关键部分,线程就能够放弃该锁并将关联条件设置为新的条件。 条件自己是任意的:您能够根据应用程序的须要定义它们。
#pragma mark -- NSConditionLock
- (void)td_testConditonLock{
// 信号量
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[conditionLock lockWhenCondition:1]; // conditoion = 1 内部 Condition 匹配
// -[NSConditionLock lockWhenCondition: beforeDate:]
NSLog(@"线程 1");
[conditionLock unlockWithCondition:0];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[conditionLock lockWhenCondition:2];
sleep(0.1);
NSLog(@"线程 2");
// self.myLock.value = 1;
[conditionLock unlockWithCondition:1]; // _value = 2 -> 1
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lock];
NSLog(@"线程 3");
[conditionLock unlock];
});
}
复制代码
NSConditionLock
的源码其实就是NSCondition
和NSLock
结合封装的一把锁