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
(两个状态参数:ACQUIRE
与RELEASE
) - 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_enter
和objc_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 锁的底层原理的主要内容,如果未能解决你的问题,请参考以下文章