@synchronized是线程同步锁,易用、可读性高。
@synchronized(self) { 临界区 }
利用如下命令将其重写
clang -rewrite-objc file
得到C++实现
{ id _sync_obj = (id)self; objc_sync_enter(_sync_obj); struct _SYNC_EXIT { _SYNC_EXIT(id arg) : sync_exit(arg) {} ~_SYNC_EXIT() { objc_sync_exit(sync_exit); } id sync_exit; } _sync_exit(_sync_obj); }
深究objc_sync_enter和objc_sync_exit的源码
int objc_sync_enter(id obj) { if (obj) { SyncData* data = id2data(obj, ACQUIRE); recursive_mutex_lock(&data->mutex); // 获取递归锁 } else { // @synchronized(nil) does nothing } }
int objc_sync_exit(id obj) { if (obj) { SyncData* data = id2data(obj, RELEASE); recursive_mutex_unlock(&data->mutex); // 释放递归锁 } else { // @synchronized(nil) does nothing } }
可以看到
1、@synchronized底层用的是递归锁,即同个线程可重入,而不会导致死锁。
2、@synchronized(nil)是不会上锁的。
接着,显然SyncList是单链表,SyncData是其中的节点,而整体的存储则是一个“拉链哈希表”。
typedef struct SyncData { struct SyncData* nextData; // 指向下一个SyncData节点 DisguisedPtr<objc_object> object; // @synchronized的参数,即上面的self recursive_mutex_t mutex; // 递归锁 } SyncData; struct SyncList { SyncData *data; // 单链表的头指针 spinlock_t lock; // 自旋锁,性能好,但不绝对安全 SyncList() : data(nil) { } }; #define LOCK_FOR_OBJ(obj) sDataLists[obj].lock // 根据参数获取单链表的锁 #define LIST_FOR_OBJ(obj) sDataLists[obj].data // 根据参数获取单链表 static StripedMap<SyncList> sDataLists; // 哈希表
// 根据参数获取对应的SyncData节点,其实就是哈希表查找 static SyncData* id2data(id object, enum usage why) { spinlock_t *lockp = &LOCK_FOR_OBJ(object); SyncData **listp = &LIST_FOR_OBJ(object); // 参数对应的单链表 SyncData* result = NULL; lockp->lock(); { SyncData* p; // 遍历单链表 for (p = *listp; p != NULL; p = p->nextData) { if ( p->object == object ) { // 找到参数对应的SyncData节点 result = p; goto done; } } } // 单链表中还没有参数对应的SyncData节点 result = (SyncData*)calloc(sizeof(SyncData), 1); result->object = (objc_object *)object; // 填充数据 new (&result->mutex) recursive_mutex_t(); // 新建的SyncData节点往链表头部加 result->nextData = *listp; *listp = result; done: lockp->unlock(); return result; }
template<typename T> class StripedMap { static unsigned int indexForPointer(const void *p) { // 取参数地址的哈希值作为数组的index uintptr_t addr = reinterpret_cast<uintptr_t>(p); return ((addr >> 4) ^ (addr >> 9)) % StripeCount; } public: T& operator[] (const void *p) { return array[indexForPointer(p)].value; } };
可以看出,参数和递归锁是一一对应的。因此,一定要保证参数不变,这里的不变,指的是完全不变,包括地址。
源码:
https://opensource.apple.com/source/objc4/objc4-680/runtime/objc-sync.mm