在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
@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
|
请发表评论