iOS 之 锁总结

一、 iOS 中常用的锁实现方式
锁分为 互斥锁(递归锁和非递归锁) 和 自旋锁(忙等) iOS中的锁都是对pthread的封装。

  1. @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+±- 可以很好的解决死锁 的现象

  1. 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 来解决
  1. NSRecursiveLock 递归锁 在有递归调用的时候使用
	NSRecursiveLock *lock = [[NSRecursiveLock alloc]init];
	[lock lock];
    [lock unlock];

这种情况下如果再有多线程访问时容易出现 死锁 此时synchronized 就派上用场了 尽管说效率会低些

  1. NSCondition 条件锁
self.condition = [[NSCondition alloc]init];
[_condition lock];
[_condition unlock];
[self.condition signal]; // 相当于释放当前条件 
[self.condition wait]; // 阻塞等待当前线程 直到 signal 释放条件
  1. 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 释放当前锁 并且会把条件重新设置

  1. 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的

今天终于把锁给搞明白了 收获不小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值