iOS 锁的底层原理

Posted WeaterMr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS 锁的底层原理相关的知识,希望对你有一定的参考价值。

@synchronized(互斥锁) 原理

1.clang分析实现原理

  {
        objc_sync_enter(_sync_obj);
        try {
            // 结构体
            struct _SYNC_EXIT {
            // 构造函数
            _SYNC_EXIT(id arg) : sync_exit(arg) {}
            // 析构函数
            ~_SYNC_EXIT() {objc_sync_exit(sync_exit);}
             id sync_exit;
           } 
           _sync_exit(_sync_obj);
        }
    }

分析:

  • 1.objc_sync_enter(_sync_obj);
  • 2._SYNC_EXIT 结构体
  • 3._sync_exit(_sync_obj);

objc_sync_enter 函数探索

int objc_sync_enter(id obj){
    int result = OBJC_SYNC_SUCCESS;
    if (obj) {
        // obj 存在
        SyncData* data = id2data(obj, ACQUIRE);
        ASSERT(data);
        data->mutex.lock();
    } else {
        // obj 不存在 什么事情也不做
    }
    return result;
}

分析:

  • 1.被锁对象obj 为空的时候都是什么事情也不会做。
  • 2.核心函数id2data (两个状态参数:ACQUIRERELEASE
  • 3.SyncData 的数据结构。

SyncData 数据结构

typedef struct alignas(CacheLineSize) SyncData {
    struct SyncData* nextData;
    DisguisedPtr<objc_object> object;
    int32_t threadCount;     // 使用此块的线程数
    recursive_mutex_t mutex; //递归锁
} SyncData;

分析:

  • 1.nextData 判断为单向链表,头插法。
  • 2.threadCount 多线程标记
  • 3.mutex 递归锁

id2data 函数实现分析

struct SyncList {
    SyncData *data;
    spinlock_t lock;
    constexpr SyncList() : data(nil), lock(fork_unsafe_lock) { }
};
// 使用多个并行列表来减少不相关对象之间的争用
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
// 全局静态变量   哈希表
static StripedMap<SyncList> sDataLists;
// 哈希函数 确定一个下标
// 可能会发生冲突 (之前通过在哈希来解决)
// 这里 使用 拉链法
...

static SyncData* id2data(id object, enum usage why)
{
    spinlock_t *lockp = &LOCK_FOR_OBJ(object);
    SyncData **listp = &LIST_FOR_OBJ(object);
    SyncData* result = NULL;
    
#if SUPPORT_DIRECT_THREAD_KEYS
    //支持线程局部存储
    
    // 检查每个线程的单条目快速缓存是否匹配对象
    bool fastCacheOccupied = NO;
    SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
    
    // 第一部分
    if (data) { ... }
    
#endif
    
    // 检查已经拥有的锁的每个线程缓存是否匹配对象
    SyncCache *cache = fetch_cache(NO);
    
    // 第二部分
    if (cache) { ... }
    
    //线程缓存没有找到任何东西
    //遍历正在使用的列表以查找匹配的对象
    //自旋锁防止多个线程创建多个
    //锁定相同的新对象
    //我们可以把节点保存在某个哈希表中,如果有的话 
    //有20多个不同的锁在工作中,但我们现在不这么做。
    
    
    //这里的锁 是为了保证在开辟内存空间时候的安全, 和外面的锁不一样哦, 此处是一个 spinlock_t 在上面有定义
    lockp->lock(); 
    
    // 第三部分
    // 代码块
    { ... }
    
    // 分配一个新的SyncData并添加到列表中.
    // XXX在持有全局锁的情况下分配内存是不好的做法,
    // 可能值得释放锁,重新分配,再搜索一次.
    // 但由于我们从不释放这些人我们不会经常陷入分配中.
    
    // 第四部分
    posix_memalign((void **)&result, alignof(SyncData), sizeof(SyncData)); 
    result->object = (objc_object *)object;
    result->threadCount = 1;
    new (&result->mutex) recursive_mutex_t(fork_unsafe_lock);
    result->nextData = *listp;
    *listp = result;
    
    
done:

    lockp->unlock(); //这里的锁 是为了保证在开辟内存空间时候的安全, 和外面的锁不一样哦,此处是一个 spinlock_t 在上面有定义
    
    // 第五部分
    if (result) { ... }
    
    return result;
}
  • 1: 创建一张全局的哈希表(static StripedMap sDataLists;), 使用拉链法来存储 SyncData;这里的全局表中存放不同对象,拉链拉的是相同对象的不同线程SyncData
  • 2: sDataLists, 中array 存储的是 SyncList (绑定的是objc,我们加锁的对象)
  • 3: objc_sync_enterobjc_sync_exit 调用对称,封装的是递归锁
  • 4: 两种存储 :TLS(线程局部存储) / cache(线程缓存)
  • 5: 第一次 syncData 头插法 链表 标记 threadCount = 1
  • 6: 判断是否同一个对象:如果是,lockCount++
  • 7: TLS找到 -> lock ++
  • 8: TLS 找不到 sync threadCount ++
  • 9: lock — threadCount—
    Synchronized : 可重入 递归 多线程
  • 1: TLS 保障 threadCount 多少条线程对这个锁对象加锁
  • 2: lock ++ 进来多少次

以上是关于iOS 锁的底层原理的主要内容,如果未能解决你的问题,请参考以下文章

iOS底层探索之多线程(十三)—锁的种类你知多少?

[iOS开发]weak底层原理

epoll底层原理总结

Java并发编程:底层实现机制

synchronized 底层如何实现?啥是锁的升级,降级

JVM技术专题「源码专题」深入剖析JVM的Mutex锁的运行原理及源码实现(底层原理-防面试)