一、 iOS 中常用的锁实现方式
锁分为 互斥锁(递归锁和非递归锁) 和 自旋锁(忙等) iOS中的锁都是对pthread的封装。
- @synchronized (self) 通过断点调试 看汇编 发现调用如下两个方法 objc_sync_enter、objc_sync_exit。源码实现如下
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
assert(data);
data->mutex.lock();
} else {
// @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;
}
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
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 {
// @synchronized(nil) does nothing
}
return result;
}
解释
当加锁对象是nil时 走 objc_sync_nil(); 什么都不做 相当于没有加锁 搭配使用可以有效的防止死锁
再来看 id2data 部分源码
static SyncData* id2data(id object, enum usage why)
{
spinlock_t *lockp = &LOCK_FOR_OBJ(object);
SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
if (data) {}
SyncCache *cache = fetch_cache(NO);
if (cache) {}
}
LOCK_FOR_OBJ
struct SyncList {
SyncData *data;
spinlock_t lock;
constexpr SyncList() : data(nil), lock(fork_unsafe_lock) { }
};
// Use multiple parallel lists to decrease contention among unrelated objects.
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap<SyncList> sDataLists;
通过上述代码可以总结 synchronized 是互斥锁的一种递归封装 是一种SyncList表结构 存储一个个的节点 SyncData 通过对threadCount++ – lockCount+±- 可以很好的解决死锁 的现象
- NSLock 互斥锁的简单封装 容易出现阻塞现象
NSLock *lock = [[NSLock alloc]init];
[lock lock];
[lock unlock];
阻塞举例
- (void)testRecursive{
NSLock *lock = [[NSLock alloc]init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testMethod)(int);
testMethod = ^(int index){
[lock lock];
if (index > 0){
NSLog(@"------testMethod---%d", index);
testMethod(index - 1);
}
[lock lock];
};
testMethod(10);
});
}
此处只会打印10。然后阻塞. 解决办法就使用 NSRecursiveLock 来解决
- NSRecursiveLock 递归锁 在有递归调用的时候使用
NSRecursiveLock *lock = [[NSRecursiveLock alloc]init];
[lock lock];
[lock unlock];
这种情况下如果再有多线程访问时容易出现 死锁 此时synchronized 就派上用场了 尽管说效率会低些
- NSCondition 条件锁
self.condition = [[NSCondition alloc]init];
[_condition lock];
[_condition unlock];
[self.condition signal]; // 相当于释放当前条件
[self.condition wait]; // 阻塞等待当前线程 直到 signal 释放条件
- NSConditionLock 输出结果 321 或 213
NSConditionLock *conditionLock = [[NSConditionLock alloc]initWithCondition:2];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lockWhenCondition:1];
NSLog(@"---testConditionLock---1");
[conditionLock unlockWithCondition:0];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lockWhenCondition:2];
NSLog(@"---testConditionLock---2");
[conditionLock unlockWithCondition:1];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lock];
NSLog(@"---testConditionLock---3");
[conditionLock unlock];
});
lockWhenCondition 当条件满足时才会执行
unlockWithCondition 释放当前锁 并且会把条件重新设置
- nonatomic 和 atomic 是自旋锁
实现原理 是 对setter和getter方法进行加锁 加锁不了+操作
@property(atomic, assign)NSInteger count;
- (void)testAtomic{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 2000; i ++){
self.count += 1;
}
NSLog(@"-----_count-----%ld", (long)self.count);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 2000; i ++){
self.count += 1;
}
NSLog(@"-----_count-----%ld", (long)self.count);
});
}
输出结果肯定是小于4000的
今天终于把锁给搞明白了 收获不小。