Objective-C @synchronized深入理解
@synchronized是线程同步锁,易用、可读性高。
1 2 3 | @synchronized ( self ) { 临界区 } |
利用如下命令将其重写
1 | clang -rewrite-objc file |
得到C++实现
1 2 3 4 5 6 7 8 9 10 11 | { 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的源码
1 2 3 4 5 6 7 8 9 | int objc_sync_enter( id obj) { if (obj) { SyncData* data = id2data(obj, ACQUIRE); recursive_mutex_lock(&data->mutex); // 获取递归锁 } else { // @synchronized(nil) does nothing } } |
1 2 3 4 5 6 7 8 9 | 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是其中的节点,而整体的存储则是一个“拉链哈希表”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; // 哈希表 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // 根据参数获取对应的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; } |
1 2 3 4 5 6 7 8 9 10 11 12 | 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
分类:
客户端
标签:
@synchronized
, 递归锁
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!