锁的种类
- xcrun编译main.m得到.cpp文件,xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
精简后流程为
- id _sync_obj = (id)appDelegateClassName
- objc_sync_enter(_sync_obj)
- objc_sync_exit(_sync_obj)
@synchronized == objc_sync_enter(obj)和objc_sync_exit (obj) ```cpp int main(int argc, char argv[]) { NSString appDelegateClassName; @autoreleasepool { // Setup code that might create autoreleased objects goes here. appDelegateClassName = NSStringFromClass([AppDelegate class]); @synchronized (appDelegateClassName) {
} } return UIApplicationMain(argc, argv, nil, appDelegateClassName); }
appDelegateClassName = NSStringFromClass(((Class ()(id, SEL))(void )objc_msgSend)((id)objc_getClass(“AppDelegate”), sel_registerName(“class”)));
{
id _rethrow = 0;
id _sync_obj = (id)appDelegateClassName;
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);
} catch (id e) {
_rethrow = e;
}
…
}
<a name="AOO9W"></a>### 底层源码<a name="K35pP"></a>#### objc_sync_enter- **参数为nil时,什么也没做,dose nothing**- **通过id2data获取data(将id类型的obj封装data),然后进行lock**- **※SyncData* data = id2data(obj, ACQUIRE)**```cppint 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 nothingif (DebugNilSync) {_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");}objc_sync_nil();}return result;}
objc_sync_exit
- 通过id2data获取data(将obj封装data),然后进行unlock
重点就在SyncData数据结构及id2data方法的调用,与objc_sync_enter不同的是,这里参数传递了RELEASE ```cpp 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;
}
<a name="DdmIw"></a>#### SyncData- **SyncData底层是一个结构体,单链表**- **struct SyncData* nextData -> 相同的数据类型,本质一个单链表**- **DisguisedPtr<objc_object> object -> 类似**[**关联对象**](https://www.yuque.com/raindykukude/fwbpv1/nn2o70#IUewU)**中对object的封装,计算方便,无符号长整型 -> value**- **threadCount -> 代表了可多线程,多少线程对同一对象进行加锁**- **mutex -> 递归锁**```cpptypedef struct alignas(CacheLineSize) SyncData {struct SyncData* nextData;DisguisedPtr<objc_object> object;int32_t threadCount; // number of THREADS using this blockrecursive_mutex_t mutex;} SyncData;
id2data
#if SUPPORT_DIRECT_THREAD_KEYS 判断是否支持TLS(局部线程存储,iOS多线程),如果支持则进行栈存储,否则进行cache存储
static SyncData* id2data(id object, enum usage why){// 从hash表中获取object对应的锁,防止对线程同时操作spinlock_t *lockp = &LOCK_FOR_OBJ(object); //宏定义 获取sDataLists的lockp// 从hash表中获取object对应的SyncData的地址SyncData **listp = &LIST_FOR_OBJ(object); //宏定义 获取sDataLists的dataSyncData* result = NULL;#if SUPPORT_DIRECT_THREAD_KEYS //是否支持TLS// Check per-thread single-entry fast cache for matching objectbool fastCacheOccupied = NO;// 支持TLS时,从线程的局部存储获取dataSyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);if (data) {...}#endif// 从线程缓存中查找// Check per-thread cache of already-owned locks for matching objectSyncCache *cache = fetch_cache(NO);if (cache) {...}...}
TLS存储与cache存储代码类似,以TLS为例,首先会判断传入的对象与缓存对象是否一致,如果一致才进行lockCount++或者lockCount—操作,然后更新当前缓存
当lockCount为0时,OSAtomicDecrement32Barrier(&result->threadCount),表示当前线程空间已经解锁
bool fastCacheOccupied = NO;SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);if (data) {fastCacheOccupied = YES;if (data->object == object) {// Found a match in fast cache.uintptr_t lockCount;result = data;lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);if (result->threadCount <= 0 || lockCount <= 0) {_objc_fatal("id2data fastcache is buggy");}switch(why) {case ACQUIRE: {lockCount++;tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);break;}case RELEASE:lockCount--;tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);if (lockCount == 0) {// remove from fast cachetls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);// atomic because may collide with concurrent ACQUIREOSAtomicDecrement32Barrier(&result->threadCount);}break;case CHECK:// do nothingbreak;}return result;}}
TLS及Cache都没有缓存时,开辟一个新的SyncData并添加到表中,只有一个syncData,所以threadCount为1
- result->nextData = listp,listp = result,头插法,每次新建的SyncData插入在头部,然后listp指向头部
static SyncData* id2data(id object, enum usage why){...// Allocate a new SyncData and add to list.// XXX allocating memory with a global lock held is bad practice,// might be worth releasing the lock, allocating, and searching again.// But since we never free these guys we won't be stuck in allocation very often.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;...}
整体流程
- 首先如果支持线程栈存储查找,就从TLS(线程局部存储)中查找SyncData,走相应的流程
- 如果不支持TLS,则在T线程缓存中查找,如果缓存中有走缓存中的流程
- 缓存中没有则判断哈希表中是否有存储对应的SyncData,如果SyncData存在则进行对线程操作同一对象的流程
- 如果以上都没有,则表示第一次进来,创建SyncData
sDataLists
- sDataLists是一个数组,存储了全局静态变量StripedMap,全局的Hash表,在真机情况array个数是8,其它环境64个
- StripedMap是一个模板,根据key(void *)hash取value(hashMap)
- SyncList,是一个结构体,由SyncData和spinlock_t构成
SyncCache,结构体,每个线程缓存对应一个SyncCache.SyncCacheItem表示每个对象锁的信息。
- SyncCacheItem,结构体,包含了SyncData和lockCount当前线程当前对象锁的次数
tls查找流程static StripedMap<SyncList> sDataLists
- SyncCacheItem,结构体,包含了SyncData和lockCount当前线程当前对象锁的次数
首先在tls查到data,如果data有值fastCacheOccupied = YES
- 如果data->object == object表示加锁的是同一个对象,此时把在tls中查找的data赋值给result
- 如果why是ACQUIRE表示加锁,此时lockCount++,并把lockCount更新到tls中
- 如果why是RELEASE表示解锁,此时lockCount—,并把lockCount更新到tls中,如果lockCount == 0表示当前线程中没有加锁的对象或者已经全部解锁。此时threadCount减1,返回result
如果data->object和object不是同一个对象则进行线程缓存查找流程
哈希函数
哈希函数通过下标获取到对应value,当出现冲突时,可以进行再hash,还可以进行拉链法,例如下图中的syncData
- 可递归性 -> @synchronized锁同一个对象时,形成了拉链的数据结构,其key都是同一个对象
- 在cache时,首先判断当前对象锁了多少次,进行lockCount++或lockCount—,当lockCount为0时,表示当前线程对对象的加锁没有了,但其它线程对该对象可能有加锁,所有@synchronized可多线程

- 线程空间相互独立,可以对同一对象加锁,threadCount代表了对个线程对同一对象的加锁
总结
- @synchronized,可重入多线程可递归。threadCount表示多线程,lockCount表示被一个线程锁多少次。
- TLS保障threadCount,多少条线程对这个对象加锁 -> 同一线程走到了TLS这里
- lock++ 记录进来多少次
- sync是全局的Hash表,采用拉链法syncData
- sDataList arry 存储的是Synclist(objc)
- objc_sync_enter/exit 对称存在,封装的是递归锁
- 两种存储:TLS、Cache
- syncData 第一次,进行头插法创建链表结构,标记threadCount为1
- 判断是不是同一个对象
- TLS->lock++
- TLS找不到sync,threadCount++
- lock解锁,threadCount—
注意:锁的对象不要为空,另外锁对象的生命周期尽量保持全局性,一般传入self,方便存储和释放
头插法图解
